Skip to content

Commit 1b6dab8

Browse files
vovastyrcancro
authored andcommitted
support cancelation in ASGraphicsCreateImage (TextureGroup#1814)
* support cancelation in ASGraphicsCreateImage * updated comment
1 parent 4222a5f commit 1b6dab8

3 files changed

Lines changed: 89 additions & 12 deletions

File tree

Source/Details/ASGraphicsContext.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ NS_ASSUME_NONNULL_BEGIN
3030
*/
3131
AS_EXTERN UIImage *ASGraphicsCreateImageWithOptions(CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t NS_NOESCAPE _Nullable isCancelled, void (NS_NOESCAPE ^work)(void)) ASDISPLAYNODE_DEPRECATED_MSG("Use ASGraphicsCreateImageWithTraitCollectionAndOptions instead");
3232

33+
/**
34+
* A wrapper for the UIKit drawing APIs. If you are in ASExperimentalDrawingGlobal, and you have iOS >= 10, we will create
35+
* a UIGraphicsRenderer with an appropriate format. Otherwise, we will use UIGraphicsBeginImageContext et al.
36+
*
37+
* @param traitCollection Trait collection. The `work` block will be executed with this trait collection, so it will affect dynamic colors, etc.
38+
* @param size The size of the context.
39+
* @param opaque Whether the context should be opaque or not.
40+
* @param scale The scale of the context. 0 uses main screen scale.
41+
* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its
42+
* preferred renderer format if we are using UIGraphicsImageRenderer.
43+
* @param isCancelled An optional block for canceling the drawing before forming the image.
44+
* @param work A block, wherein the current UIGraphics context is set based on the arguments.
45+
*
46+
* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext.
47+
*/
48+
AS_EXTERN UIImage *ASGraphicsCreateImage(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t _Nullable NS_NOESCAPE isCancelled, void (NS_NOESCAPE ^work)(void));
49+
3350
/**
3451
* A wrapper for the UIKit drawing APIs.
3552
*

Source/Details/ASGraphicsContext.mm

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515

1616
#if AS_AT_LEAST_IOS13
17-
#define PERFORM_WORK_WITH_TRAIT_COLLECTION(work, traitCollection) \
17+
#define ASPerformBlockWithTraitCollection(work, traitCollection) \
1818
if (@available(iOS 13.0, *)) { \
1919
UITraitCollection *uiTraitCollection = ASPrimitiveTraitCollectionToUITraitCollection(traitCollection); \
2020
[uiTraitCollection performAsCurrentTraitCollection:^{ \
@@ -24,7 +24,7 @@
2424
work(); \
2525
}
2626
#else
27-
#define PERFORM_WORK_WITH_TRAIT_COLLECTION(work, traitCollection) work();
27+
#define ASPerformBlockWithTraitCollection(work, traitCollection) work();
2828
#endif
2929

3030

@@ -43,10 +43,10 @@ NS_INLINE void ASConfigureExtendedRange(UIGraphicsImageRendererFormat *format)
4343
asdisplaynode_iscancelled_block_t NS_NOESCAPE isCancelled,
4444
void (^NS_NOESCAPE work)())
4545
{
46-
return ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollectionMakeDefault(), size, opaque, scale, sourceImage, work);
46+
return ASGraphicsCreateImage(ASPrimitiveTraitCollectionMakeDefault(), size, opaque, scale, sourceImage, isCancelled, work);
4747
}
4848

49-
UIImage *ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * sourceImage, void (NS_NOESCAPE ^work)()) {
49+
UIImage *ASGraphicsCreateImage(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * sourceImage, asdisplaynode_iscancelled_block_t NS_NOESCAPE isCancelled, void (NS_NOESCAPE ^work)()) {
5050
if (AS_AVAILABLE_IOS_TVOS(10, 10)) {
5151
if (ASActivateExperimentalFeature(ASExperimentalDrawingGlobal)) {
5252
// If they used default scale, reuse one of two preferred formats.
@@ -98,17 +98,39 @@ NS_INLINE void ASConfigureExtendedRange(UIGraphicsImageRendererFormat *format)
9898
ASConfigureExtendedRange(format);
9999
}
100100

101-
return [[[UIGraphicsImageRenderer alloc] initWithSize:size format:format] imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
102-
ASDisplayNodeCAssert(UIGraphicsGetCurrentContext(), @"Should have a context!");
103-
PERFORM_WORK_WITH_TRAIT_COLLECTION(work, traitCollection)
104-
}];
101+
// Avoid using the imageWithActions: method because it does not support cancellation at the
102+
// last moment i.e. before actually creating the resulting image.
103+
__block UIImage *image;
104+
NSError *error;
105+
[[[UIGraphicsImageRenderer alloc] initWithSize:size format:format]
106+
runDrawingActions:^(UIGraphicsImageRendererContext *rendererContext) {
107+
ASDisplayNodeCAssert(UIGraphicsGetCurrentContext(), @"Should have a context!");
108+
ASPerformBlockWithTraitCollection(work, traitCollection);
109+
}
110+
completionActions:^(UIGraphicsImageRendererContext *rendererContext) {
111+
if (isCancelled == nil || !isCancelled()) {
112+
image = rendererContext.currentImage;
113+
}
114+
}
115+
error:&error];
116+
if (error) {
117+
NSCAssert(NO, @"Error drawing: %@", error);
118+
}
119+
return image;
105120
}
106121
}
107122

108123
// Bad OS or experiment flag. Use UIGraphics* API.
109124
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
110-
PERFORM_WORK_WITH_TRAIT_COLLECTION(work, traitCollection)
111-
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
125+
ASPerformBlockWithTraitCollection(work, traitCollection)
126+
UIImage *image = nil;
127+
if (isCancelled == nil || !isCancelled()) {
128+
image = UIGraphicsGetImageFromCurrentImageContext();
129+
}
112130
UIGraphicsEndImageContext();
113131
return image;
114132
}
133+
134+
UIImage *ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * sourceImage, void (NS_NOESCAPE ^work)()) {
135+
return ASGraphicsCreateImage(traitCollection, size, opaque, scale, sourceImage, nil, work);
136+
}

Tests/ASGraphicsContextTests.mm

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,44 @@ - (void)setUp
2828

2929

3030
#if AS_AT_LEAST_IOS13
31+
- (void)testCanceled
32+
{
33+
if (AS_AVAILABLE_IOS_TVOS(13, 13)) {
34+
CGSize size = CGSize{.width=100, .height=100};
35+
36+
XCTestExpectation *expectationCancelled = [self expectationWithDescription:@"canceled"];
37+
38+
asdisplaynode_iscancelled_block_t isCancelledBlock =^BOOL{
39+
[expectationCancelled fulfill];
40+
return true;
41+
};
42+
43+
ASPrimitiveTraitCollection traitCollection = ASPrimitiveTraitCollectionMakeDefault();
44+
UIImage *canceledImage = ASGraphicsCreateImage(traitCollection, size, false, 0, nil, isCancelledBlock, ^{});
45+
46+
XCTAssertNil(canceledImage);
47+
48+
[self waitForExpectations:@[expectationCancelled] timeout:1];
49+
}
50+
}
51+
52+
- (void)testCanceledNil
53+
{
54+
if (AS_AVAILABLE_IOS_TVOS(13, 13)) {
55+
CGSize size = CGSize{.width=100, .height=100};
56+
ASPrimitiveTraitCollection traitCollection = ASPrimitiveTraitCollectionMakeDefault();
57+
58+
XCTestExpectation *expectation = [self expectationWithDescription:@"normal"];
59+
UIImage *image = ASGraphicsCreateImage(traitCollection, size, false, 0, nil, nil, ^{
60+
[expectation fulfill];
61+
});
62+
63+
XCTAssert(image);
64+
65+
[self waitForExpectations:@[expectation] timeout:1];
66+
}
67+
}
68+
3169
- (void)testTraitCollectionPassedToWork
3270
{
3371
if (AS_AVAILABLE_IOS_TVOS(13, 13)) {
@@ -36,7 +74,7 @@ - (void)testTraitCollectionPassedToWork
3674
XCTestExpectation *expectationDark = [self expectationWithDescription:@"trait collection dark"];
3775
ASPrimitiveTraitCollection traitCollectionDark = ASPrimitiveTraitCollectionMakeDefault();
3876
traitCollectionDark.userInterfaceStyle = UIUserInterfaceStyleDark;
39-
ASGraphicsCreateImageWithTraitCollectionAndOptions(traitCollectionDark, size, false, 0, nil, ^{
77+
ASGraphicsCreateImage(traitCollectionDark, size, false, 0, nil, nil, ^{
4078
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
4179
XCTAssertEqual(currentTraitCollection.userInterfaceStyle, UIUserInterfaceStyleDark);
4280
[expectationDark fulfill];
@@ -45,7 +83,7 @@ - (void)testTraitCollectionPassedToWork
4583
XCTestExpectation *expectationLight = [self expectationWithDescription:@"trait collection light"];
4684
ASPrimitiveTraitCollection traitCollectionLight = ASPrimitiveTraitCollectionMakeDefault();
4785
traitCollectionLight.userInterfaceStyle = UIUserInterfaceStyleLight;
48-
ASGraphicsCreateImageWithTraitCollectionAndOptions(traitCollectionLight, size, false, 0, nil, ^{
86+
ASGraphicsCreateImage(traitCollectionLight, size, false, 0, nil, nil, ^{
4987
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
5088
XCTAssertEqual(currentTraitCollection.userInterfaceStyle, UIUserInterfaceStyleLight);
5189
[expectationLight fulfill];

0 commit comments

Comments
 (0)