Adopt UIGraphicsImageRenderer API

Summary:
Apple suggested to this new API on iOS 10+.

> // Any new bitmap drawing code is encouraged to use UIGraphicsImageRenderer in lieu of this API.

WWDC18 Reference: https://developer.apple.com/videos/play/wwdc2018/219/
> Use UIGraphicsImageRenderer to create and draw to an image buffer
Supports Wide Color, unlike UIGraphicsBeginImageContext()
Combine with UIImageView for efficient offscreen rendering

Per https://nshipster.com/image-resizing/#performance-benchmarks, the new API runs even faster than the C version, probably due to more smart context reuses/management.

Changelog:
[iOS][Changed] - Adopt UIGraphicsImageRenderer API

Reviewed By: philIip

Differential Revision: D35699584

fbshipit-source-id: 7a1e2109d5e121fb396c1014f4ed0a892211b0cc
This commit is contained in:
Chaoshuai Lu 2022-04-22 17:02:51 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 7454044542
Коммит d70d7fd0b3
6 изменённых файлов: 262 добавлений и 257 удалений

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

@ -22,10 +22,13 @@ UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
if (CGImageGetBitsPerPixel(imageRef) != 32 ||
CGImageGetBitsPerComponent(imageRef) != 8 ||
!((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, inputImage.scale);
[inputImage drawAtPoint:CGPointZero];
imageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage;
UIGraphicsEndImageContext();
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.scale = inputImage.scale;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:inputImage.size format:rendererFormat];
imageRef = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
[inputImage drawAtPoint:CGPointZero];
}].CGImage;
}
vImage_Buffer buffer1, buffer2;
@ -51,7 +54,7 @@ UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
//create temp buffer
vImage_Error tempBufferSize = vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, NULL, 0, 0, boxSize, boxSize,
NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
if (tempBufferSize < 0) {
free(buffer1.data);
free(buffer2.data);

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

@ -370,13 +370,14 @@ UIImage *__nullable RCTTransformImage(UIImage *image,
}
BOOL opaque = !RCTImageHasAlpha(image.CGImage);
UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextConcatCTM(currentContext, transform);
[image drawAtPoint:CGPointZero];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.opaque = opaque;
rendererFormat.scale = destScale;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:destSize format:rendererFormat];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
CGContextConcatCTM(context.CGContext, transform);
[image drawAtPoint:CGPointZero];
}];
}
BOOL RCTImageHasAlpha(CGImageRef image)

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

@ -172,13 +172,15 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn
return RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
}
static CGContextRef
RCTUIGraphicsBeginImageContext(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge)
static UIGraphicsImageRenderer *
RCTUIGraphicsImageRenderer(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge)
{
const CGFloat alpha = CGColorGetAlpha(backgroundColor);
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
return UIGraphicsGetCurrentContext();
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.opaque = opaque;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:rendererFormat];
return renderer;
}
static UIImage *RCTGetSolidBorderImage(
@ -229,164 +231,166 @@ static UIImage *RCTGetSolidBorderImage(
edgeInsets.top + 1 + edgeInsets.bottom
} : viewSize;
CGContextRef ctx = RCTUIGraphicsBeginImageContext(size, backgroundColor, hasCornerRadii, drawToEdge);
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
UIGraphicsImageRenderer *const imageRenderer =
RCTUIGraphicsImageRenderer(size, backgroundColor, hasCornerRadii, drawToEdge);
UIImage *image = [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
const CGContextRef context = rendererContext.CGContext;
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
if (backgroundColor) {
CGContextSetFillColorWithColor(ctx, backgroundColor);
CGContextAddPath(ctx, path);
CGContextFillPath(ctx);
}
if (backgroundColor) {
CGContextSetFillColorWithColor(context, backgroundColor);
CGContextAddPath(context, path);
CGContextFillPath(context);
}
CGContextAddPath(ctx, path);
CGPathRelease(path);
CGContextAddPath(context, path);
CGPathRelease(path);
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets, NULL);
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets, NULL);
CGContextAddPath(ctx, insetPath);
CGContextEOClip(ctx);
CGContextAddPath(context, insetPath);
CGContextEOClip(context);
BOOL hasEqualColors = RCTBorderColorsAreEqual(borderColors);
if ((drawToEdge || !hasCornerRadii) && hasEqualColors) {
CGContextSetFillColorWithColor(ctx, borderColors.left);
CGContextAddRect(ctx, rect);
CGContextAddPath(ctx, insetPath);
CGContextEOFillPath(ctx);
BOOL hasEqualColors = RCTBorderColorsAreEqual(borderColors);
if ((drawToEdge || !hasCornerRadii) && hasEqualColors) {
CGContextSetFillColorWithColor(context, borderColors.left);
CGContextAddRect(context, rect);
CGContextAddPath(context, insetPath);
CGContextEOFillPath(context);
} else {
CGPoint topLeft = (CGPoint){borderInsets.left, borderInsets.top};
if (cornerInsets.topLeft.width > 0 && cornerInsets.topLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){topLeft, {2 * cornerInsets.topLeft.width, 2 * cornerInsets.topLeft.height}},
CGPointZero,
topLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
topLeft = points[1];
} else {
CGPoint topLeft = (CGPoint){borderInsets.left, borderInsets.top};
if (cornerInsets.topLeft.width > 0 && cornerInsets.topLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){topLeft, {2 * cornerInsets.topLeft.width, 2 * cornerInsets.topLeft.height}},
CGPointZero,
topLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
topLeft = points[1];
}
}
}
CGPoint bottomLeft = (CGPoint){borderInsets.left, size.height - borderInsets.bottom};
if (cornerInsets.bottomLeft.width > 0 && cornerInsets.bottomLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{bottomLeft.x, bottomLeft.y - 2 * cornerInsets.bottomLeft.height},
{2 * cornerInsets.bottomLeft.width, 2 * cornerInsets.bottomLeft.height}},
(CGPoint){0, size.height},
bottomLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
bottomLeft = points[1];
CGPoint bottomLeft = (CGPoint){borderInsets.left, size.height - borderInsets.bottom};
if (cornerInsets.bottomLeft.width > 0 && cornerInsets.bottomLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{bottomLeft.x, bottomLeft.y - 2 * cornerInsets.bottomLeft.height},
{2 * cornerInsets.bottomLeft.width, 2 * cornerInsets.bottomLeft.height}},
(CGPoint){0, size.height},
bottomLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
bottomLeft = points[1];
}
}
}
CGPoint topRight = (CGPoint){size.width - borderInsets.right, borderInsets.top};
if (cornerInsets.topRight.width > 0 && cornerInsets.topRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{topRight.x - 2 * cornerInsets.topRight.width, topRight.y},
{2 * cornerInsets.topRight.width, 2 * cornerInsets.topRight.height}},
(CGPoint){size.width, 0},
topRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
topRight = points[0];
CGPoint topRight = (CGPoint){size.width - borderInsets.right, borderInsets.top};
if (cornerInsets.topRight.width > 0 && cornerInsets.topRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{topRight.x - 2 * cornerInsets.topRight.width, topRight.y},
{2 * cornerInsets.topRight.width, 2 * cornerInsets.topRight.height}},
(CGPoint){size.width, 0},
topRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
topRight = points[0];
}
}
}
CGPoint bottomRight = (CGPoint){size.width - borderInsets.right, size.height - borderInsets.bottom};
if (cornerInsets.bottomRight.width > 0 && cornerInsets.bottomRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{bottomRight.x - 2 * cornerInsets.bottomRight.width, bottomRight.y - 2 * cornerInsets.bottomRight.height},
{2 * cornerInsets.bottomRight.width, 2 * cornerInsets.bottomRight.height}},
(CGPoint){size.width, size.height},
bottomRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
bottomRight = points[0];
CGPoint bottomRight = (CGPoint){size.width - borderInsets.right, size.height - borderInsets.bottom};
if (cornerInsets.bottomRight.width > 0 && cornerInsets.bottomRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){
{bottomRight.x - 2 * cornerInsets.bottomRight.width,
bottomRight.y - 2 * cornerInsets.bottomRight.height},
{2 * cornerInsets.bottomRight.width, 2 * cornerInsets.bottomRight.height}},
(CGPoint){size.width, size.height},
bottomRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
bottomRight = points[0];
}
}
}
CGColorRef currentColor = NULL;
CGColorRef currentColor = NULL;
// RIGHT
if (borderInsets.right > 0) {
const CGPoint points[] = {
(CGPoint){size.width, 0},
topRight,
bottomRight,
(CGPoint){size.width, size.height},
};
// RIGHT
if (borderInsets.right > 0) {
const CGPoint points[] = {
(CGPoint){size.width, 0},
topRight,
bottomRight,
(CGPoint){size.width, size.height},
};
currentColor = borderColors.right;
CGContextAddLines(ctx, points, sizeof(points) / sizeof(*points));
}
// BOTTOM
if (borderInsets.bottom > 0) {
const CGPoint points[] = {
(CGPoint){0, size.height},
bottomLeft,
bottomRight,
(CGPoint){size.width, size.height},
};
if (!CGColorEqualToColor(currentColor, borderColors.bottom)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.bottom;
currentColor = borderColors.right;
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
CGContextAddLines(ctx, points, sizeof(points) / sizeof(*points));
}
// LEFT
if (borderInsets.left > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
bottomLeft,
(CGPoint){0, size.height},
};
// BOTTOM
if (borderInsets.bottom > 0) {
const CGPoint points[] = {
(CGPoint){0, size.height},
bottomLeft,
bottomRight,
(CGPoint){size.width, size.height},
};
if (!CGColorEqualToColor(currentColor, borderColors.left)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.left;
if (!CGColorEqualToColor(currentColor, borderColors.bottom)) {
CGContextSetFillColorWithColor(context, currentColor);
CGContextFillPath(context);
currentColor = borderColors.bottom;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
CGContextAddLines(ctx, points, sizeof(points) / sizeof(*points));
}
// TOP
if (borderInsets.top > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
topRight,
(CGPoint){size.width, 0},
};
// LEFT
if (borderInsets.left > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
bottomLeft,
(CGPoint){0, size.height},
};
if (!CGColorEqualToColor(currentColor, borderColors.top)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.top;
if (!CGColorEqualToColor(currentColor, borderColors.left)) {
CGContextSetFillColorWithColor(context, currentColor);
CGContextFillPath(context);
currentColor = borderColors.left;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
CGContextAddLines(ctx, points, sizeof(points) / sizeof(*points));
// TOP
if (borderInsets.top > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
topRight,
(CGPoint){size.width, 0},
};
if (!CGColorEqualToColor(currentColor, borderColors.top)) {
CGContextSetFillColorWithColor(context, currentColor);
CGContextFillPath(context);
currentColor = borderColors.top;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
CGContextSetFillColorWithColor(context, currentColor);
CGContextFillPath(context);
}
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
}
CGPathRelease(insetPath);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGPathRelease(insetPath);
}];
if (makeStretchable) {
image = [image resizableImageWithCapInsets:edgeInsets];
@ -477,42 +481,41 @@ static UIImage *RCTGetDashedOrDottedBorderImage(
}
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
CGContextRef ctx = RCTUIGraphicsBeginImageContext(viewSize, backgroundColor, hasCornerRadii, drawToEdge);
const CGRect rect = {.size = viewSize};
UIGraphicsImageRenderer *const imageRenderer =
RCTUIGraphicsImageRenderer(viewSize, backgroundColor, hasCornerRadii, drawToEdge);
return [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
const CGContextRef context = rendererContext.CGContext;
const CGRect rect = {.size = viewSize};
if (backgroundColor) {
CGPathRef outerPath = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
CGContextAddPath(ctx, outerPath);
CGPathRelease(outerPath);
if (backgroundColor) {
CGPathRef outerPath = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
CGContextAddPath(context, outerPath);
CGPathRelease(outerPath);
CGContextSetFillColorWithColor(ctx, backgroundColor);
CGContextFillPath(ctx);
}
CGContextSetFillColorWithColor(context, backgroundColor);
CGContextFillPath(context);
}
// Stroking means that the width is divided in half and grows in both directions
// perpendicular to the path, that's why we inset by half the width, so that it
// reaches the edge of the rect.
CGRect pathRect = CGRectInset(rect, lineWidth / 2.0, lineWidth / 2.0);
CGPathRef path = RCTPathCreateWithRoundedRect(pathRect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
// Stroking means that the width is divided in half and grows in both directions
// perpendicular to the path, that's why we inset by half the width, so that it
// reaches the edge of the rect.
CGRect pathRect = CGRectInset(rect, lineWidth / 2.0, lineWidth / 2.0);
CGPathRef path = RCTPathCreateWithRoundedRect(pathRect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
CGFloat dashLengths[2];
dashLengths[0] = dashLengths[1] = (borderStyle == RCTBorderStyleDashed ? 3 : 1) * lineWidth;
CGFloat dashLengths[2];
dashLengths[0] = dashLengths[1] = (borderStyle == RCTBorderStyleDashed ? 3 : 1) * lineWidth;
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetLineDash(ctx, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths));
CGContextSetLineWidth(context, lineWidth);
CGContextSetLineDash(context, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths));
CGContextSetStrokeColorWithColor(ctx, [UIColor yellowColor].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextAddPath(ctx, path);
CGContextSetStrokeColorWithColor(ctx, borderColors.top);
CGContextStrokePath(ctx);
CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, borderColors.top);
CGContextStrokePath(context);
CGPathRelease(path);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
CGPathRelease(path);
}];
}
UIImage *RCTGetBorderImage(

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

@ -19,65 +19,69 @@ RCT_EXPORT_METHOD(takeScreenshot
: (RCTPromiseResolveBlock)resolve reject
: (RCTPromiseRejectBlock)reject)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
// Get view
UIView *view;
if (target == nil || [target isEqual:@"window"]) {
view = RCTKeyWindow();
} else if ([target isKindOfClass:[NSNumber class]]) {
view = viewRegistry[target];
if (!view) {
RCTLogError(@"No view found with reactTag: %@", target);
return;
}
}
[self.bridge.uiManager addUIBlock:^(
__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
// Get view
UIView *view;
if (target == nil || [target isEqual:@"window"]) {
view = RCTKeyWindow();
} else if ([target isKindOfClass:[NSNumber class]]) {
view = viewRegistry[target];
if (!view) {
RCTLogError(@"No view found with reactTag: %@", target);
return;
}
}
// Get options
CGSize size = [RCTConvert CGSize:options];
NSString *format = [RCTConvert NSString:options[@"format"] ?: @"png"];
// Get options
CGSize size = [RCTConvert CGSize:options];
NSString *format = [RCTConvert NSString:options[@"format"] ?: @"png"];
// Capture image
if (size.width < 0.1 || size.height < 0.1) {
size = view.bounds.size;
}
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
BOOL success = [view drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Capture image
if (size.width < 0.1 || size.height < 0.1) {
size = view.bounds.size;
}
if (!success || !image) {
reject(RCTErrorUnspecified, @"Failed to capture view snapshot.", nil);
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:rendererFormat];
__block BOOL success = NO;
UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
success = [view drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES];
}];
if (!success || !image) {
reject(RCTErrorUnspecified, @"Failed to capture view snapshot.", nil);
return;
}
// Convert image to data (on a background thread)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data;
if ([format isEqualToString:@"png"]) {
data = UIImagePNGRepresentation(image);
} else if ([format isEqualToString:@"jpeg"]) {
CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1];
data = UIImageJPEGRepresentation(image, quality);
} else {
RCTLogError(@"Unsupported image format: %@", format);
return;
}
// Save to a temp file
NSError *error = nil;
NSString *tempFilePath = RCTTempFilePath(format, &error);
if (tempFilePath) {
if ([data writeToFile:tempFilePath options:(NSDataWritingOptions)0 error:&error]) {
resolve(tempFilePath);
return;
}
}
// Convert image to data (on a background thread)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data;
if ([format isEqualToString:@"png"]) {
data = UIImagePNGRepresentation(image);
} else if ([format isEqualToString:@"jpeg"]) {
CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1];
data = UIImageJPEGRepresentation(image, quality);
} else {
RCTLogError(@"Unsupported image format: %@", format);
return;
}
// Save to a temp file
NSError *error = nil;
NSString *tempFilePath = RCTTempFilePath(format, &error);
if (tempFilePath) {
if ([data writeToFile:tempFilePath options:(NSDataWritingOptions)0 error:&error]) {
resolve(tempFilePath);
return;
}
}
// If we reached here, something went wrong
reject(RCTErrorUnspecified, error.localizedDescription, error);
});
}];
// If we reached here, something went wrong
reject(RCTErrorUnspecified, error.localizedDescription, error);
});
}];
}
@end

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

@ -325,23 +325,14 @@ typedef NS_ENUM(NSInteger, FBTestSnapshotFileNameType) {
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
NSAssert1(context, @"Could not generate context for view %@", view);
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:bounds.size
format:rendererFormat];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
{
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
BOOL success = [view drawViewHierarchyInRect:bounds afterScreenUpdates:YES];
NSAssert1(success, @"Could not create snapshot for view %@", view);
}
CGContextRestoreGState(context);
UIGraphicsPopContext();
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}];
}
@end

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

@ -15,19 +15,22 @@
return nil;
}
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
CGContextSetAlpha(context, 0.5f);
CGContextBeginTransparencyLayer(context, NULL);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextSetBlendMode(context, kCGBlendModeDifference);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
CGContextEndTransparencyLayer(context);
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImage;
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.opaque = YES;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize
format:rendererFormat];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
CGContextSetAlpha(context.CGContext, 0.5f);
CGContextBeginTransparencyLayer(context.CGContext, NULL);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextSetBlendMode(context.CGContext, kCGBlendModeDifference);
CGContextSetFillColorWithColor(context.CGContext, [UIColor whiteColor].CGColor);
CGContextFillRect(context.CGContext, CGRectMake(0, 0, self.size.width, self.size.height));
CGContextEndTransparencyLayer(context.CGContext);
}];
}
@end