Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.
Merged
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
179 changes: 87 additions & 92 deletions AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
}
}

#define DISPLAY_COUNT_INCREMENT() __ASDisplayLayerIncrementConcurrentDisplayCount(asynchronous, rasterizing);
#define DISPLAY_COUNT_DECREMENT() __ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
#define CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT(expr) if (isCancelledBlock()) { \
expr; \
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing); \
return nil; \
} \

#define CHECK_CANCELLED_AND_RETURN_NIL(expr) if (isCancelledBlock()) { \
expr; \
return nil; \
} \


- (NSObject *)drawParameters
{
if (_flags.implementsDrawParameters) {
Expand Down Expand Up @@ -179,141 +193,122 @@ - (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode
}
}

- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock rasterizing:(BOOL)rasterizing
- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous
isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock
rasterizing:(BOOL)rasterizing
{
asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil;
ASDisplayNodeFlags flags;

__instanceLock__.lock();

flags = _flags;
__instanceLock__.unlock();

ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container.");
// We always create a graphics context, unless a -display method is used, OR if we are a subnode drawing into a rasterized parent.
BOOL shouldCreateGraphicsContext = (flags.implementsInstanceImageDisplay == NO && flags.implementsImageDisplay == NO && rasterizing == NO);
BOOL shouldBeginRasterizing = (rasterizing == NO && flags.shouldRasterizeDescendants);
BOOL usesInstanceMethodDisplay = (flags.implementsInstanceDrawRect || flags.implementsInstanceImageDisplay);
BOOL usesImageDisplay = (flags.implementsImageDisplay || flags.implementsInstanceImageDisplay);
BOOL usesDrawRect = (flags.implementsDrawRect || flags.implementsInstanceDrawRect);

if (usesImageDisplay == NO && usesDrawRect == NO && shouldBeginRasterizing == NO) {
// Early exit before requesting more expensive properties like bounds and opaque from the layer.
__instanceLock__.unlock();
return nil;
}

BOOL opaque = self.opaque;
CGRect bounds = self.bounds;
CGFloat contentsScaleForDisplay = _contentsScaleForDisplay;

if (!rasterizing && self.shouldRasterizeDescendants) {
CGRect bounds = self.bounds;
if (CGRectIsEmpty(bounds)) {
return nil;
}
// Capture drawParameters from delegate on main thread, if this node is displaying itself rather than recursively rasterizing.
id drawParameters = (shouldBeginRasterizing == NO ? [self drawParameters] : nil);

__instanceLock__.unlock();

// Only the -display methods should be called if we can't size the graphics buffer to use.
if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) {
return nil;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to entirely eliminate this third condition, and thus use just one else {

ASDisplayNodeAssert(contentsScaleForDisplay != 0.0, @"Invalid contents scale");
ASDisplayNodeAssert(usesInstanceMethodDisplay == NO || (flags.implementsDrawRect == NO && flags.implementsImageDisplay == NO),
@"Node %@ should not implement both class and instance method display or draw", self);
ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized),
@"Rasterized descendants should never display unless being drawn into the rasterized container.");

if (shouldBeginRasterizing == YES) {
// Collect displayBlocks for all descendants.
NSMutableArray *displayBlocks = [NSMutableArray array];
[self _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks];

CGFloat contentsScaleForDisplay = self.contentsScaleForDisplay;
BOOL opaque = self.opaque && CGColorGetAlpha(self.backgroundColor.CGColor) == 1.0f;

ASDisplayNodeAssert(self.contentsScaleForDisplay != 0.0, @"Invalid contents scale");
CHECK_CANCELLED_AND_RETURN_NIL();

// If [UIColor clearColor] or another semitransparent background color is used, include alpha channel when rasterizing.
// Unlike CALayer drawing, we include the backgroundColor as a base during rasterization.
opaque = opaque && CGColorGetAlpha(self.backgroundColor.CGColor) == 1.0f;

displayBlock = ^id{
__ASDisplayLayerIncrementConcurrentDisplayCount(asynchronous, rasterizing);
if (isCancelledBlock()) {
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return nil;
}

ASDN_DELAY_FOR_DISPLAY();
DISPLAY_COUNT_INCREMENT();
CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT();

UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);

for (dispatch_block_t block in displayBlocks) {
if (isCancelledBlock()) {
UIGraphicsEndImageContext();
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return nil;
}
CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT(UIGraphicsEndImageContext());
block();
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);

return image;
};
} else if (flags.implementsInstanceImageDisplay || flags.implementsImageDisplay) {
// Capture drawParameters from delegate on main thread
id drawParameters = [self drawParameters];

displayBlock = ^id{
__ASDisplayLayerIncrementConcurrentDisplayCount(asynchronous, rasterizing);
if (isCancelledBlock()) {
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return nil;
}

ASDN_DELAY_FOR_DISPLAY();

UIImage *result = nil;
//We can't call _willDisplayNodeContentWithRenderingContext or _didDisplayNodeContentWithRenderingContext because we don't
//have a context. We rely on implementors of displayWithParameters:isCancelled: to call
if (flags.implementsInstanceImageDisplay) {
result = [self displayWithParameters:drawParameters isCancelled:isCancelledBlock];
} else {
result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock];
}
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return result;
DISPLAY_COUNT_DECREMENT();
return image;
};

} else if (flags.implementsInstanceDrawRect || flags.implementsDrawRect) {

CGRect bounds = self.bounds;
if (CGRectIsEmpty(bounds)) {
return nil;
}

// Capture drawParameters from delegate on main thread
id drawParameters = [self drawParameters];
CGFloat contentsScaleForDisplay = self.contentsScaleForDisplay;
BOOL opaque = self.opaque;

} else {
displayBlock = ^id{
__ASDisplayLayerIncrementConcurrentDisplayCount(asynchronous, rasterizing);
if (isCancelledBlock()) {
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return nil;
}
DISPLAY_COUNT_INCREMENT();
CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT();

ASDN_DELAY_FOR_DISPLAY();

if (!rasterizing) {
if (shouldCreateGraphicsContext) {
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT( UIGraphicsEndImageContext(); );
}

CGContextRef currentContext = UIGraphicsGetCurrentContext();
UIImage *image = nil;

// For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or
// _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs.
if (currentContext && _willDisplayNodeContentWithRenderingContext) {
_willDisplayNodeContentWithRenderingContext(currentContext);
}

if (flags.implementsInstanceDrawRect) {
[self drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
} else {
[[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
// Decide if we use a class or instance method to draw or display.
id object = usesInstanceMethodDisplay ? self : [self class];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This secretly makes me happy, which is probably bad.


if (usesImageDisplay) { // If we are using a display method, we'll get an image back directly.
image = [object displayWithParameters:drawParameters
isCancelled:isCancelledBlock];
} else if (usesDrawRect) { // If we're using a draw method, this will operate on the currentContext.
[object drawRect:bounds withParameters:drawParameters
isCancelled:isCancelledBlock isRasterizing:rasterizing];
}

if (currentContext && _didDisplayNodeContentWithRenderingContext) {
_didDisplayNodeContentWithRenderingContext(currentContext);
}

if (isCancelledBlock()) {
if (!rasterizing) {
UIGraphicsEndImageContext();
}
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return nil;
}

UIImage *image = nil;
if (!rasterizing) {

if (shouldCreateGraphicsContext) {
CHECK_CANCELLED_AND_RETURN_NIL_WITH_DECREMENT( UIGraphicsEndImageContext(); );
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}

__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);

ASDN_DELAY_FOR_DISPLAY();
DISPLAY_COUNT_DECREMENT();
return image;
};

}

return [displayBlock copy];
Expand Down Expand Up @@ -356,7 +351,7 @@ - (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asy
ASDisplayNodeCAssertMainThread();
if (!canceled && !isCancelledBlock()) {
UIImage *image = (UIImage *)value;
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
if (stretchable) {
ASDisplayNodeSetupLayerContentsWithResizableImage(_layer, image);
} else {
Expand Down