Merge pull request #1132 from garrettmoon/switchToInstanceDrawAndDisplay

[ASDisplayNode+AsyncDisplay] Implement instance methods of draw and display, use for text and image performance boost.
This commit is contained in:
appleguy
2016-01-28 16:17:31 -08:00
9 changed files with 236 additions and 115 deletions

View File

@@ -20,4 +20,21 @@
*/
- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously;
/**
* @abstract allow modification of a context before the node's content is drawn
*
* @discussion Set the block to be called after the context has been created and before the node's content is drawn.
* You can override this to modify the context before the content is drawn. You are responsible for saving and
* restoring context if necessary. Restoring can be done in contextDidDisplayNodeContent
* This block can be called from *any* thread and it is unsafe to access any UIKit main thread properties from it.
*/
@property (nonatomic, strong) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext;
/**
* @abstract allow modification of a context after the node's content is drawn
*
* @discussion
*/
@property (nonatomic, strong) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
@end

View File

@@ -37,6 +37,11 @@ typedef CALayer * _Nonnull(^ASDisplayNodeLayerBlock)();
*/
typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode * _Nonnull node);
/**
* ASDisplayNode will / did render node content in context.
*/
typedef void (^ASDisplayNodeContextModifier)(_Nonnull CGContextRef context);
/**
Interface state is available on ASDisplayNode and ASViewController, and
allows checking whether a node is in an interface situation where it is prudent to trigger certain

View File

@@ -110,8 +110,12 @@ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *i
flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
if (instance) {
flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
flags.implementsInstanceDrawRect = ([instance respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
flags.implementsInstanceImageDisplay = ([instance respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
} else {
flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
flags.implementsInstanceDrawRect = ([c instancesRespondToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
flags.implementsInstanceImageDisplay = ([c instancesRespondToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
}
return flags;
}
@@ -1491,7 +1495,7 @@ static NSInteger incrementIfFound(NSInteger i) {
// Helper method to summarize whether or not the node run through the display process
- (BOOL)__implementsDisplay
{
return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants;
return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants || _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
}
- (void)_setupPlaceholderLayer

View File

@@ -14,6 +14,7 @@
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import "ASImageNode+CGExtras.h"
@@ -22,42 +23,34 @@
@interface _ASImageNodeDrawParameters : NSObject
@property (nonatomic, assign, readonly) BOOL cropEnabled;
@property (nonatomic, assign) BOOL opaque;
@property (nonatomic, retain) UIImage *image;
@property (nonatomic, assign) CGRect bounds;
@property (nonatomic, assign) CGFloat contentsScale;
@property (nonatomic, retain) UIColor *backgroundColor;
@property (nonatomic, assign) UIViewContentMode contentMode;
@property (nonatomic, assign) CGRect cropRect;
@property (nonatomic, copy) asimagenode_modification_block_t imageModificationBlock;
@end
// TODO: eliminate explicit parameters with a set of keys copied from the node
@implementation _ASImageNodeDrawParameters
- (id)initWithCrop:(BOOL)cropEnabled opaque:(BOOL)opaque image:(UIImage *)image bounds:(CGRect)bounds contentsScale:(CGFloat)contentsScale backgroundColor:(UIColor *)backgroundColor contentMode:(UIViewContentMode)contentMode cropRect:(CGRect)cropRect imageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock
- (id)initWithBounds:(CGRect)bounds opaque:(BOOL)opaque contentsScale:(CGFloat)contentsScale backgroundColor:(UIColor *)backgroundColor contentMode:(UIViewContentMode)contentMode
{
self = [self init];
if (!self) return nil;
_cropEnabled = cropEnabled;
_opaque = opaque;
_image = image;
_bounds = bounds;
_contentsScale = contentsScale;
_backgroundColor = backgroundColor;
_contentMode = contentMode;
_cropRect = cropRect;
_imageModificationBlock = [imageModificationBlock copy];
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ : %p image:%@ cropEnabled:%@ opaque:%@ bounds:%@ contentsScale:%.2f backgroundColor:%@ contentMode:%@ cropRect:%@>", [self class], self, self.image, @(self.cropEnabled), @(self.opaque), NSStringFromCGRect(self.bounds), self.contentsScale, self.backgroundColor, ASDisplayNodeNSStringFromUIContentMode(self.contentMode), NSStringFromCGRect(self.cropRect)];
return [NSString stringWithFormat:@"<%@ : %p opaque:%@ bounds:%@ contentsScale:%.2f backgroundColor:%@ contentMode:%@>", [self class], self, @(self.opaque), NSStringFromCGRect(self.bounds), self.contentsScale, self.backgroundColor, ASDisplayNodeNSStringFromUIContentMode(self.contentMode)];
}
@end
@@ -78,6 +71,7 @@
}
@synthesize image = _image;
@synthesize imageModificationBlock = _imageModificationBlock;
- (id)init
{
@@ -151,90 +145,110 @@
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
{
BOOL hasValidCropBounds = _cropEnabled && !CGRectIsNull(_cropDisplayBounds) && !CGRectIsEmpty(_cropDisplayBounds);
return [[_ASImageNodeDrawParameters alloc] initWithCrop:_cropEnabled
opaque:self.opaque
image:self.image
bounds:(hasValidCropBounds ? _cropDisplayBounds : self.bounds)
contentsScale:self.contentsScaleForDisplay
backgroundColor:self.backgroundColor
contentMode:self.contentMode
cropRect:self.cropRect
imageModificationBlock:self.imageModificationBlock];
return [[_ASImageNodeDrawParameters alloc] initWithBounds:self.bounds
opaque:self.opaque
contentsScale:self.contentsScaleForDisplay
backgroundColor:self.backgroundColor
contentMode:self.contentMode];
}
+ (UIImage *)displayWithParameters:(_ASImageNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
- (UIImage *)displayWithParameters:(_ASImageNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
{
UIImage *image = parameters.image;
if (!image) {
return nil;
UIImage *image;
BOOL cropEnabled;
CGFloat contentsScale;
CGRect cropDisplayBounds;
CGRect cropRect;
asimagenode_modification_block_t imageModificationBlock;
{
ASDN::MutexLocker l(_imageLock);
image = _image;
if (!image) {
return nil;
}
cropEnabled = _cropEnabled;
contentsScale = _contentsScaleForDisplay;
cropDisplayBounds = _cropDisplayBounds;
cropRect = _cropRect;
imageModificationBlock = _imageModificationBlock;
}
ASDisplayNodeAssert(parameters.contentsScale > 0, @"invalid contentsScale at display time");
ASDisplayNodeContextModifier preContextBlock = self.willDisplayNodeContentWithRenderingContext;
ASDisplayNodeContextModifier postContextBlock = self.didDisplayNodeContentWithRenderingContext;
BOOL hasValidCropBounds = cropEnabled && !CGRectIsNull(cropDisplayBounds) && !CGRectIsEmpty(cropDisplayBounds);
CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : parameters.bounds);
BOOL isOpaque = parameters.opaque;
UIColor *backgroundColor = parameters.backgroundColor;
UIViewContentMode contentMode = parameters.contentMode;
ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time");
// if the image is resizable, bail early since the image has likely already been configured
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (stretchable) {
if (parameters.imageModificationBlock != NULL) {
image = parameters.imageModificationBlock(image);
if (imageModificationBlock != NULL) {
image = imageModificationBlock(image);
}
return image;
}
CGRect bounds = parameters.bounds;
CGFloat contentsScale = parameters.contentsScale;
UIViewContentMode contentMode = parameters.contentMode;
CGSize imageSize = image.size;
CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale);
CGSize boundsSizeInPixels = CGSizeMake(floorf(bounds.size.width * contentsScale), floorf(bounds.size.height * contentsScale));
BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill
|| contentMode == UIViewContentModeScaleAspectFit
|| contentMode == UIViewContentModeCenter;
|| contentMode == UIViewContentModeScaleAspectFit
|| contentMode == UIViewContentModeCenter;
CGSize backingSize;
CGRect imageDrawRect;
if (boundsSizeInPixels.width * contentsScale < 1.0f ||
boundsSizeInPixels.height * contentsScale < 1.0f ||
imageSizeInPixels.width < 1.0f ||
imageSizeInPixels.height < 1.0f) {
return nil;
}
// If we're not supposed to do any cropping, just decode image at original size
if (!parameters.cropEnabled || !contentModeSupported || stretchable) {
if (!cropEnabled || !contentModeSupported || stretchable) {
backingSize = imageSizeInPixels;
imageDrawRect = (CGRect){.size = backingSize};
} else {
ASCroppedImageBackingSizeAndDrawRectInBounds(imageSizeInPixels,
boundsSizeInPixels,
contentMode,
parameters.cropRect,
cropRect,
&backingSize,
&imageDrawRect);
}
if (backingSize.width <= 0.0f ||
backingSize.height <= 0.0f ||
imageDrawRect.size.width <= 0.0f ||
imageDrawRect.size.height <= 0.0f) {
return nil;
}
// Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds
// will do its rounding on pixel instead of point boundaries
UIGraphicsBeginImageContextWithOptions(backingSize, parameters.opaque, 1.0);
UIGraphicsBeginImageContextWithOptions(backingSize, isOpaque, 1.0);
// if view is opaque, fill the context with background color
if (parameters.opaque && parameters.backgroundColor) {
[parameters.backgroundColor setFill];
if (isOpaque && backgroundColor) {
[backgroundColor setFill];
UIRectFill({ .size = backingSize });
}
CGContextRef context = UIGraphicsGetCurrentContext();
if (context && preContextBlock) {
preContextBlock(context);
}
// iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on
// multiple threads concurrently. In fact, instead of crashing, it appears to deadlock.
// The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premier,
@@ -250,20 +264,24 @@
@synchronized(image) {
[image drawInRect:imageDrawRect];
}
if (context && postContextBlock) {
postContextBlock(context);
}
if (isCancelled()) {
UIGraphicsEndImageContext();
return nil;
}
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (parameters.imageModificationBlock != NULL) {
result = parameters.imageModificationBlock(result);
if (imageModificationBlock != NULL) {
result = imageModificationBlock(result);
}
return result;
}
@@ -271,12 +289,21 @@
{
[super displayDidFinish];
// If we've got a block to perform after displaying, do it.
if (self.image && _displayCompletionBlock) {
ASDN::MutexLocker l(_imageLock);
// FIXME: _displayCompletionBlock is not protected by lock
_displayCompletionBlock(NO);
void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock;
UIImage *image = _image;
ASDN::MutexLocker u(_imageLock);
// If we've got a block to perform after displaying, do it.
if (image && displayCompletionBlock) {
displayCompletionBlock(NO);
ASDN::MutexLocker l(_imageLock);
_displayCompletionBlock = nil;
ASDN::MutexLocker u(_imageLock);
}
}
@@ -290,7 +317,7 @@
}
// Stash the block and call-site queue. We'll invoke it in -displayDidFinish.
// FIXME: _displayCompletionBlock not protected by lock
ASDN::MutexLocker l(_imageLock);
if (_displayCompletionBlock != displayCompletionBlock) {
_displayCompletionBlock = [displayCompletionBlock copy];
}
@@ -301,6 +328,7 @@
#pragma mark - Cropping
- (BOOL)isCropEnabled
{
ASDN::MutexLocker l(_imageLock);
return _cropEnabled;
}
@@ -311,6 +339,7 @@
- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds
{
ASDN::MutexLocker l(_imageLock);
if (_cropEnabled == cropEnabled)
return;
@@ -331,11 +360,13 @@
- (CGRect)cropRect
{
ASDN::MutexLocker l(_imageLock);
return _cropRect;
}
- (void)setCropRect:(CGRect)cropRect
{
ASDN::MutexLocker l(_imageLock);
if (CGRectEqualToRect(_cropRect, cropRect))
return;
@@ -354,6 +385,18 @@
});
}
- (asimagenode_modification_block_t)imageModificationBlock
{
ASDN::MutexLocker l(_imageLock);
return _imageModificationBlock;
}
- (void)setImageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock
{
ASDN::MutexLocker l(_imageLock);
_imageModificationBlock = imageModificationBlock;
}
@end

View File

@@ -35,13 +35,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
@interface ASTextNodeDrawParameters : NSObject
- (instancetype)initWithRenderer:(ASTextKitRenderer *)renderer
textOrigin:(CGPoint)textOrigin
backgroundColor:(UIColor *)backgroundColor;
@property (nonatomic, strong, readonly) ASTextKitRenderer *renderer;
@property (nonatomic, assign, readonly) CGPoint textOrigin;
@property (nonatomic, assign, readonly) CGRect bounds;
@property (nonatomic, strong, readonly) UIColor *backgroundColor;
@@ -49,30 +43,16 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
@implementation ASTextNodeDrawParameters
- (instancetype)initWithRenderer:(ASTextKitRenderer *)renderer
textOrigin:(CGPoint)textOrigin
backgroundColor:(UIColor *)backgroundColor
- (instancetype)initWithBounds:(CGRect)bounds
backgroundColor:(UIColor *)backgroundColor
{
if (self = [super init]) {
_renderer = renderer;
_textOrigin = textOrigin;
_bounds = bounds;
_backgroundColor = backgroundColor;
}
return self;
}
- (void)dealloc
{
// Destruction of the layout managers/containers/text storage is quite
// expensive, and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
__block ASTextKitRenderer *renderer = _renderer;
ASPerformBlockOnBackgroundThread(^{
renderer = nil;
});
_renderer = nil;
}
@end
@interface ASTextNode () <UIGestureRecognizerDelegate, NSLayoutManagerDelegate>
@@ -242,11 +222,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
#pragma mark - Renderer Management
//only safe to call on the main thread because self.bounds is only safe to call on the main thread one our node is loaded
- (ASTextKitRenderer *)_renderer
{
return [self _rendererWithBounds:self.bounds];
}
- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds
{
ASDN::MutexLocker l(_rendererLock);
if (_renderer == nil) {
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : self.bounds.size;
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size;
_renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes]
constrainedSize:constrainedSize];
}
@@ -435,13 +421,18 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
#pragma mark - Drawing
+ (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
- (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
{
CGContextRef context = UIGraphicsGetCurrentContext();
ASDisplayNodeAssert(context, @"This is no good without a context.");
CGContextSaveGState(context);
ASTextKitRenderer *renderer = [self _rendererWithBounds:parameters.bounds];
UIEdgeInsets shadowPadding = [self shadowPaddingWithRenderer:renderer];
CGPoint boundsOrigin = parameters.bounds.origin;
CGPoint textOrigin = CGPointMake(boundsOrigin.x - shadowPadding.left, boundsOrigin.y - shadowPadding.top);
// Fill background
if (!isRasterizing) {
UIColor *backgroundColor = parameters.backgroundColor;
@@ -450,28 +441,20 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
UIRectFillUsingBlendMode(CGContextGetClipBoundingBox(context), kCGBlendModeCopy);
}
}
// Draw shadow
[[parameters.renderer shadower] setShadowInContext:context];
[[renderer shadower] setShadowInContext:context];
// Draw text
bounds.origin = parameters.textOrigin;
[parameters.renderer drawInContext:context bounds:bounds];
bounds.origin = textOrigin;
[renderer drawInContext:context bounds:bounds];
CGContextRestoreGState(context);
}
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
{
CGRect bounds = self.bounds;
[self _invalidateRendererIfNeededForBoundsSize:bounds.size];
// Offset the text origin by any shadow padding
UIEdgeInsets shadowPadding = [self shadowPadding];
CGPoint textOrigin = CGPointMake(bounds.origin.x - shadowPadding.left, bounds.origin.y - shadowPadding.top);
return [[ASTextNodeDrawParameters alloc] initWithRenderer:[self _renderer]
textOrigin:textOrigin
backgroundColor:self.backgroundColor];
return [[ASTextNodeDrawParameters alloc] initWithBounds:self.bounds backgroundColor:self.backgroundColor];
}
#pragma mark - Attributes
@@ -1031,9 +1014,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
}
}
//only safe to call on main thread, because [self _renderer] is only safe to call on the main thread
- (UIEdgeInsets)shadowPadding
{
return [self _renderer].shadower.shadowPadding;
return [self shadowPaddingWithRenderer:[self _renderer]];
}
- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer
{
return renderer.shadower.shadowPadding;
}
#pragma mark - Truncation Message

View File

@@ -94,6 +94,18 @@ typedef BOOL(^asdisplaynode_iscancelled_block_t)(void);
*/
+ (UIImage *)displayWithParameters:(id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock;
/**
* @abstract instance version of drawRect class method
* @see drawRect:withParameters:isCancelled:isRasterizing class method
*/
- (void)drawRect:(CGRect)bounds withParameters:(id <NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing;
/**
* @abstract instance version of display class method
* @see displayWithParameters:isCancelled class method
*/
- (UIImage *)displayWithParameters:(id <NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled;
// Called on the main thread only
/**

View File

@@ -12,6 +12,7 @@
#import "ASAssert.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASDisplayNode+Beta.h"
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@end
@@ -224,25 +225,32 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
return image;
};
} else if (_flags.implementsImageDisplay) {
} 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 = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock];
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;
};
} else if (_flags.implementsDrawRect) {
} else if (_flags.implementsInstanceDrawRect || _flags.implementsDrawRect) {
CGRect bounds = self.bounds;
if (CGRectIsEmpty(bounds)) {
@@ -267,7 +275,20 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
}
[[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
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];
}
if (currentContext && _didDisplayNodeContentWithRenderingContext) {
_didDisplayNodeContentWithRenderingContext(currentContext);
}
if (isCancelledBlock()) {
if (!rasterizing) {
@@ -370,4 +391,28 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
[_displaySentinel increment];
}
- (ASDisplayNodeContextModifier)willDisplayNodeContentWithRenderingContext
{
ASDN::MutexLocker l(_propertyLock);
return _willDisplayNodeContentWithRenderingContext;
}
- (ASDisplayNodeContextModifier)didDisplayNodeContentWithRenderingContext
{
ASDN::MutexLocker l(_propertyLock);
return _didDisplayNodeContentWithRenderingContext;
}
- (void)setWillDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier
{
ASDN::MutexLocker l(_propertyLock);
_willDisplayNodeContentWithRenderingContext = contextModifier;
}
- (void)setDidDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier;
{
ASDN::MutexLocker l(_propertyLock);
_didDisplayNodeContentWithRenderingContext = contextModifier;
}
@end

View File

@@ -17,6 +17,7 @@
#import "ASSentinel.h"
#import "ASThread.h"
#import "ASLayoutOptions.h"
#import "_ASDisplayLayer.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -87,7 +87,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
unsigned hasCustomDrawingPriority:1;
// whether custom drawing is enabled
unsigned implementsInstanceDrawRect:1;
unsigned implementsDrawRect:1;
unsigned implementsInstanceImageDisplay:1;
unsigned implementsImageDisplay:1;
unsigned implementsDrawParameters:1;
@@ -101,6 +103,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeExtraIvars _extra;
ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext;
ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext;
#if TIME_DISPLAYNODE_OPS
@public
NSTimeInterval _debugTimeToCreateView;