diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index f287eb931f..cbf731019f 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -102,10 +102,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; if (self = [super init]) { // Load default values from superclass. _shadowOffset = [super shadowOffset]; - CGColorRef superColor = [super shadowColor]; - if (superColor != NULL) { - _shadowColor = CGColorRetain(superColor); - } + _shadowColor = CGColorRetain([super shadowColor]); _shadowOpacity = [super shadowOpacity]; _shadowRadius = [super shadowRadius]; @@ -140,10 +137,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; - (void)dealloc { - if (_shadowColor != NULL) { - CGColorRelease(_shadowColor); - } - + CGColorRelease(_shadowColor); + [self _invalidateRenderer]; if (_longPressGestureRecognizer) { @@ -548,9 +543,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; CGContextTranslateCTM(context, _textContainerInset.left, _textContainerInset.top); ASTextKitRenderer *renderer = [self _rendererWithBounds:drawParameterBounds]; - UIEdgeInsets shadowPadding = [self shadowPaddingWithRenderer:renderer]; - CGPoint boundsOrigin = drawParameterBounds.origin; - CGPoint textOrigin = CGPointMake(boundsOrigin.x - shadowPadding.left, boundsOrigin.y - shadowPadding.top); // Fill background if (backgroundColor != nil) { @@ -558,12 +550,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; UIRectFillUsingBlendMode(CGContextGetClipBoundingBox(context), kCGBlendModeCopy); } - // Draw shadow - [renderer.shadower setShadowInContext:context]; - // Draw text - bounds.origin = textOrigin; - [renderer drawInContext:context bounds:bounds]; + [renderer drawInContext:context bounds:drawParameterBounds]; CGContextRestoreGState(context); } @@ -1133,14 +1121,9 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI { ASDN::MutexLocker l(__instanceLock__); - if (_shadowColor != shadowColor) { - if (shadowColor != NULL) { - CGColorRetain(shadowColor); - } - if (_shadowColor != NULL) { - CGColorRelease(_shadowColor); - } - _shadowColor = shadowColor; + if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { + CGColorRelease(_shadowColor); + _shadowColor = CGColorRetain(shadowColor); _cachedShadowUIColor = [UIColor colorWithCGColor:shadowColor]; [self _invalidateRenderer]; [self setNeedsDisplay]; diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h index dae549e514..0021ec94cb 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h +++ b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h @@ -59,7 +59,7 @@ struct ASTextKitAttributes { /** An array of UIBezierPath objects representing the exclusion paths inside the receiver's bounding rectangle. Default value: nil. */ - NSArray *exclusionPaths; + NSArray *exclusionPaths; /** The shadow offset for any shadows applied to the text. The coordinate space for this is the same as UIKit, so a positive width means towards the right, and a positive height means towards the bottom. diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index d8ce5f84b5..0eff3ff25c 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -68,10 +68,10 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() { if (!_shadower) { ASTextKitAttributes attributes = _attributes; - _shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset - shadowColor:attributes.shadowColor - shadowOpacity:attributes.shadowOpacity - shadowRadius:attributes.shadowRadius]; + _shadower = [ASTextKitShadower shadowerWithShadowOffset:attributes.shadowOffset + shadowColor:attributes.shadowColor + shadowOpacity:attributes.shadowOpacity + shadowRadius:attributes.shadowRadius]; } return _shadower; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitShadower.h b/AsyncDisplayKit/TextKit/ASTextKitShadower.h index e8997e08e2..93dd99ffa8 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitShadower.h +++ b/AsyncDisplayKit/TextKit/ASTextKitShadower.h @@ -15,10 +15,10 @@ */ @interface ASTextKitShadower : NSObject -- (instancetype)initWithShadowOffset:(CGSize)shadowOffset - shadowColor:(UIColor *)shadowColor - shadowOpacity:(CGFloat)shadowOpacity - shadowRadius:(CGFloat)shadowRadius; ++ (ASTextKitShadower *)shadowerWithShadowOffset:(CGSize)shadowOffset + shadowColor:(UIColor *)shadowColor + shadowOpacity:(CGFloat)shadowOpacity + shadowRadius:(CGFloat)shadowRadius; /** * @abstract The offset from the top-left corner at which the shadow starts. diff --git a/AsyncDisplayKit/TextKit/ASTextKitShadower.mm b/AsyncDisplayKit/TextKit/ASTextKitShadower.mm index a4960e6699..ab961a3213 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitShadower.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitShadower.mm @@ -14,7 +14,9 @@ static inline CGSize _insetSize(CGSize size, UIEdgeInsets insets) { - return UIEdgeInsetsInsetRect({.size = size}, insets).size; + size.width -= (insets.left + insets.right); + size.height -= (insets.top + insets.bottom); + return size; } static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets) @@ -31,6 +33,28 @@ static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets) UIEdgeInsets _calculatedShadowPadding; } ++ (ASTextKitShadower *)shadowerWithShadowOffset:(CGSize)shadowOffset + shadowColor:(UIColor *)shadowColor + shadowOpacity:(CGFloat)shadowOpacity + shadowRadius:(CGFloat)shadowRadius +{ + /** + * For all cases where no shadow is drawn, we share this singleton shadower to save resources. + */ + static dispatch_once_t onceToken; + static ASTextKitShadower *sharedNonShadower; + dispatch_once(&onceToken, ^{ + sharedNonShadower = [[ASTextKitShadower alloc] initWithShadowOffset:CGSizeZero shadowColor:nil shadowOpacity:0 shadowRadius:0]; + }); + + BOOL hasShadow = shadowOpacity > 0 && (shadowRadius > 0 || CGSizeEqualToSize(shadowOffset, CGSizeZero) == NO) && CGColorGetAlpha(shadowColor.CGColor) > 0; + if (hasShadow == NO) { + return sharedNonShadower; + } else { + return [[ASTextKitShadower alloc] initWithShadowOffset:shadowOffset shadowColor:shadowColor shadowOpacity:shadowOpacity shadowRadius:shadowRadius]; + } +} + - (instancetype)initWithShadowOffset:(CGSize)shadowOffset shadowColor:(UIColor *)shadowColor shadowOpacity:(CGFloat)shadowOpacity @@ -52,7 +76,7 @@ static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets) */ - (BOOL)_shouldDrawShadow { - return _shadowOpacity != 0.0 && _shadowColor != nil && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)); + return _shadowOpacity != 0.0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor.CGColor) > 0; } - (void)setShadowInContext:(CGContextRef)context diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m index 08b953374d..a819ba2f22 100644 --- a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m @@ -92,4 +92,16 @@ ASSnapshotVerifyNode(textNode, nil); } +- (void)testShadowing +{ + ASTextNode *textNode = [[ASTextNode alloc] init]; + textNode.attributedText = [[NSAttributedString alloc] initWithString:@"Quality is Important"]; + textNode.shadowColor = [UIColor blackColor].CGColor; + textNode.shadowOpacity = 0.3; + textNode.shadowRadius = 3; + textNode.shadowOffset = CGSizeMake(0, 1); + [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))]; + ASSnapshotVerifyNode(textNode, nil); +} + @end diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png new file mode 100644 index 0000000000..edb68c14aa Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png new file mode 100644 index 0000000000..edb68c14aa Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png differ