Optimize drawing code + add examples how to round corners (#996)

* Use CoreGraphics for drawing and cropping of node content

* Smaller fixes
This commit is contained in:
Michael Schneider 2018-07-26 09:44:10 -07:00 committed by Huy Nguyen
parent 4880b54db0
commit eb4c21c545
16 changed files with 250 additions and 109 deletions

View File

@ -109,7 +109,7 @@
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; }; 509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; };
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */; }; 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */; };
509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */; }; 509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */; };
636EA1A41C7FF4EC00EE152F /* NSArray+Diffing.mm in Sources */ = {isa = PBXBuildFile; fileRef = DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */; }; 636EA1A41C7FF4EC00EE152F /* NSArray+Diffing.mm in Sources */ = {isa = PBXBuildFile; fileRef = DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */; };
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; }; 636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; };
680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -176,7 +176,7 @@
7AB338671C55B3460055FDE8 /* ASRelativeLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7AB338671C55B3460055FDE8 /* ASRelativeLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; }; 7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; };
8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */; }; 8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */; };
81E95C141D62639600336598 /* ASTextNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */; }; 81E95C141D62639600336598 /* ASTextNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */; };
83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D9591D44542100BF333E /* ASWeakMap.m */; }; 83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D9591D44542100BF333E /* ASWeakMap.m */; };
83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A7D9581D44542100BF333E /* ASWeakMap.h */; settings = {ATTRIBUTES = (Private, ); }; }; 83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A7D9581D44542100BF333E /* ASWeakMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
@ -621,7 +621,7 @@
205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutController.h; sourceTree = "<group>"; }; 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutController.h; sourceTree = "<group>"; };
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewLayoutController.m; sourceTree = "<group>"; }; 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewLayoutController.m; sourceTree = "<group>"; };
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CoreGraphics+ASConvenience.h"; sourceTree = "<group>"; }; 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CoreGraphics+ASConvenience.h"; sourceTree = "<group>"; };
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CoreGraphics+ASConvenience.m"; sourceTree = "<group>"; }; 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CoreGraphics+ASConvenience.mm"; sourceTree = "<group>"; };
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; }; 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; };
2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTruncationTests.mm; sourceTree = "<group>"; }; 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTruncationTests.mm; sourceTree = "<group>"; };
@ -734,7 +734,7 @@
7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRelativeLayoutSpec.h; sourceTree = "<group>"; }; 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRelativeLayoutSpec.h; sourceTree = "<group>"; };
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; }; 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ASConvenience.h"; sourceTree = "<group>"; }; 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ASConvenience.h"; sourceTree = "<group>"; };
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ASConvenience.m"; sourceTree = "<group>"; }; 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIImage+ASConvenience.mm"; sourceTree = "<group>"; };
81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodeSnapshotTests.m; sourceTree = "<group>"; }; 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodeSnapshotTests.m; sourceTree = "<group>"; };
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; }; 81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; }; 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
@ -1234,7 +1234,7 @@
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */, 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */, 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */,
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */, 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */,
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */, 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */,
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */, CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */,
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */, CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */,
); );
@ -1422,7 +1422,7 @@
CC3B20871C3F7A5400798563 /* ASWeakSet.h */, CC3B20871C3F7A5400798563 /* ASWeakSet.h */,
CC3B20881C3F7A5400798563 /* ASWeakSet.m */, CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */, 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */,
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */, 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */,
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */, DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */, DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */,
CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */, CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */,
@ -2405,7 +2405,7 @@
CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */, CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */,
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */,
B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */,
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */, 8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */,
CCAA0B80206ADBF30057B336 /* ASRecursiveUnfairLock.m in Sources */, CCAA0B80206ADBF30057B336 /* ASRecursiveUnfairLock.m in Sources */,
CCBDDD0620C62A2D00CBA922 /* ASMainThreadDeallocation.mm in Sources */, CCBDDD0620C62A2D00CBA922 /* ASMainThreadDeallocation.mm in Sources */,
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */, B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */,
@ -2504,7 +2504,7 @@
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */, 6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */, 68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */, CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */,
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */, 509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */,
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */, 254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */, 254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,

View File

@ -26,7 +26,7 @@
- Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler) - Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler)
- Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029) - Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029)
- Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042) - Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042)
- Optimize drawing code + add examples how to round corners. [Michael Schneider](https://github.com/maicki)
## 2.7 ## 2.7
- Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877) - Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877)

View File

@ -53,6 +53,7 @@
#import <AsyncDisplayKit/ASWeakProxy.h> #import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h> #import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import <AsyncDisplayKit/ASTipsController.h> #import <AsyncDisplayKit/ASTipsController.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds) // Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
#if TIME_DISPLAYNODE_OPS #if TIME_DISPLAYNODE_OPS
@ -1508,7 +1509,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
__instanceLock__.lock(); __instanceLock__.lock();
if (_placeholderLayer.superlayer && !placeholderShouldPersist) { if (_placeholderLayer.superlayer && !placeholderShouldPersist) {
void (^cleanupBlock)() = ^{ void (^cleanupBlock)() = ^{
[_placeholderLayer removeFromSuperlayer]; [self->_placeholderLayer removeFromSuperlayer];
}; };
if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) { if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
@ -1666,24 +1667,29 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
CGSize size = CGSizeMake(radius + 1, radius + 1); CGSize size = CGSizeMake(radius + 1, radius + 1);
ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay); ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);
CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextRef context = UIGraphicsGetCurrentContext();
if (isRight == YES) { if (isRight == YES) {
CGContextTranslateCTM(ctx, -radius + 1, 0); CGContextTranslateCTM(context, -radius + 1, 0);
} }
if (isTop == YES) { if (isTop == YES) {
CGContextTranslateCTM(ctx, 0, -radius + 1); CGContextTranslateCTM(context, 0, -radius + 1);
} }
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, radius * 2, radius * 2) cornerRadius:radius];
[roundedRect setUsesEvenOddFillRule:YES]; CGMutablePathRef roundedPath = CGPathCreateMutable();
[roundedRect appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1)]]; CGPathRef addedPath = ASCGRoundedPathCreate(CGRectMake(0, 0, radius * 2, radius * 2), radius);
[backgroundColor setFill]; CGPathAddPath(roundedPath, NULL, addedPath);
[roundedRect fill]; CGPathAddRect(roundedPath, NULL, CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1));
CGContextAddPath(context, roundedPath);
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextEOFillPath(context);
// No lock needed, as _clipCornerLayers is only modified on the main thread. // No lock needed, as _clipCornerLayers is only modified on the main thread.
CALayer *clipCornerLayer = _clipCornerLayers[idx]; _clipCornerLayers[idx].contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
clipCornerLayer.contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage); _clipCornerLayers[idx].bounds = CGRectMake(0.0, 0.0, size.width, size.height);
clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height); _clipCornerLayers[idx].anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
CGPathRelease(addedPath);
CGPathRelease(roundedPath);
} }
[self _layoutClipCornersIfNeeded]; [self _layoutClipCornersIfNeeded];
}); });
@ -1868,7 +1874,10 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
[self displayDidFinish]; [self displayDidFinish];
} }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)displayWillStart {} - (void)displayWillStart {}
#pragma clang diagnostic pop
- (void)displayWillStartAsynchronously:(BOOL)asynchronously - (void)displayWillStartAsynchronously:(BOOL)asynchronously
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
@ -2979,11 +2988,11 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) { if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) {
void(^exitVisibleInterfaceState)(void) = ^{ void(^exitVisibleInterfaceState)(void) = ^{
// This block intentionally retains self. // This block intentionally retains self.
__instanceLock__.lock(); self->__instanceLock__.lock();
unsigned isStillInHierarchy = _flags.isInHierarchy; unsigned isStillInHierarchy = self->_flags.isInHierarchy;
BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); BOOL isVisible = ASInterfaceStateIncludesVisible(self->_pendingInterfaceState);
ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); ASInterfaceState newState = (self->_pendingInterfaceState & ~ASInterfaceStateVisible);
__instanceLock__.unlock(); self->__instanceLock__.unlock();
if (!isStillInHierarchy && isVisible) { if (!isStillInHierarchy && isVisible) {
#if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR #if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR
if (![self supportsRangeManagedInterfaceState]) { if (![self supportsRangeManagedInterfaceState]) {
@ -3142,8 +3151,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
[self setDisplaySuspended:YES]; [self setDisplaySuspended:YES];
//schedule clear contents on next runloop //schedule clear contents on next runloop
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(self->__instanceLock__);
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) { if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
[self clearContents]; [self clearContents];
} }
}); });
@ -3160,8 +3169,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
[[self asyncLayer] cancelAsyncDisplay]; [[self asyncLayer] cancelAsyncDisplay];
//schedule clear contents on next runloop //schedule clear contents on next runloop
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(self->__instanceLock__);
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) { if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
[self clearContents]; [self clearContents];
} }
}); });

View File

@ -737,19 +737,43 @@ asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat
{ {
return ^(UIImage *originalImage) { return ^(UIImage *originalImage) {
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale); ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = (CGRect){CGPointZero, originalImage.size};
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, rect);
CGContextAddPath(context, path);
// Make the image round // Make the image round
[roundOutline addClip]; CGContextClip(context);
// Although drawAtPoint:blendMode: would consider the CTM already, we are using CGContext* functions for drawing
// the image instead calling drawAtPoint:blendMode. This will save use 50% of retain calls for the image
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect));
CGContextScaleCTM(context, originalImage.scale, -originalImage.scale);
CGContextSetAlpha(context, 1.0);
CGContextDrawImage(context, rect, originalImage.CGImage);
// Draw the original image CGPathRelease(path);
[originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
// Draw a border on top. // Draw a border on top.
if (borderWidth > 0.0) { if (borderWidth > 0.0) {
[borderColor setStroke]; // Begin a new path for the border
[roundOutline setLineWidth:borderWidth]; CGContextBeginPath(context);
[roundOutline stroke];
CGFloat strokeThickness = borderWidth;
CGFloat strokeInset = floor((strokeThickness + 1.0f) / 2.0f) - 1.0f;
CGPathRef path = CGPathCreateWithEllipseInRect(CGRectInset(rect, strokeInset, strokeInset), NULL);
CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
CGContextSetLineWidth(context, borderWidth);
CGContextStrokePath(context);
CGPathRelease(path);
} }
return ASGraphicsGetImageAndEndCurrentContext(); return ASGraphicsGetImageAndEndCurrentContext();

View File

@ -130,7 +130,7 @@
#endif #endif
#endif #endif
#define ASOVERLOADABLE __attribute__((overloadable)) #define AS_OVERLOADABLE __attribute__((overloadable))
#if __has_attribute(noescape) #if __has_attribute(noescape)

View File

@ -17,8 +17,8 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <tgmath.h> #import <tgmath.h>
#import <UIKit/UIBezierPath.h>
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
@ -56,4 +56,11 @@ ASDISPLAYNODE_INLINE BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CG
return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta; return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta;
}; };
AS_OVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN CGPathRef ASCGRoundedPathCreate(CGRect rect, UIRectCorner corners, CGSize cornerRadii);
AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGPathRef ASCGRoundedPathCreate(CGRect rect, CGFloat cornerRadius) {
return ASCGRoundedPathCreate(rect, UIRectCornerAllCorners, CGSizeMake(cornerRadius, cornerRadius));
}
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -1,19 +0,0 @@
//
// CoreGraphics+ASConvenience.m
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>

View File

@ -0,0 +1,64 @@
//
// CoreGraphics+ASConvenience.m
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
AS_OVERLOADABLE CGPathRef ASCGRoundedPathCreate(CGRect rect, UIRectCorner corners, CGSize cornerRadii) {
CGMutablePathRef path = CGPathCreateMutable();
const CGPoint topLeft = rect.origin;
const CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
const CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
const CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
if (corners & UIRectCornerTopLeft) {
CGPathMoveToPoint(path, NULL, topLeft.x+cornerRadii.width, topLeft.y);
} else {
CGPathMoveToPoint(path, NULL, topLeft.x, topLeft.y);
}
if (corners & UIRectCornerTopRight) {
CGPathAddLineToPoint(path, NULL, topRight.x-cornerRadii.width, topRight.y);
CGPathAddCurveToPoint(path, NULL, topRight.x, topRight.y, topRight.x, topRight.y+cornerRadii.height, topRight.x, topRight.y+cornerRadii.height);
} else {
CGPathAddLineToPoint(path, NULL, topRight.x, topRight.y);
}
if (corners & UIRectCornerBottomRight) {
CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y-cornerRadii.height);
CGPathAddCurveToPoint(path, NULL, bottomRight.x, bottomRight.y, bottomRight.x-cornerRadii.width, bottomRight.y, bottomRight.x-cornerRadii.width, bottomRight.y);
} else {
CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y);
}
if (corners & UIRectCornerBottomLeft) {
CGPathAddLineToPoint(path, NULL, bottomLeft.x+cornerRadii.width, bottomLeft.y);
CGPathAddCurveToPoint(path, NULL, bottomLeft.x, bottomLeft.y, bottomLeft.x, bottomLeft.y-cornerRadii.height, bottomLeft.x, bottomLeft.y-cornerRadii.height);
} else {
CGPathAddLineToPoint(path, NULL, bottomLeft.x, bottomLeft.y);
}
if (corners & UIRectCornerTopLeft) {
CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y+cornerRadii.height);
CGPathAddCurveToPoint(path, NULL, topLeft.x, topLeft.y, topLeft.x+cornerRadii.width, topLeft.y, topLeft.x+cornerRadii.width, topLeft.y);
} else {
CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y);
}
CGPathCloseSubpath(path);
return path;
}

View File

@ -94,7 +94,7 @@ AS_EXTERN ASDimension const ASDimensionAuto;
/** /**
* Returns a dimension with the specified type and value. * Returns a dimension with the specified type and value.
*/ */
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value) AS_OVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
{ {
if (unit == ASDimensionUnitAuto ) { if (unit == ASDimensionUnitAuto ) {
ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0."); ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0.");
@ -112,7 +112,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit
/** /**
* Returns a dimension with the specified points value. * Returns a dimension with the specified points value.
*/ */
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points) AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points)
{ {
return ASDimensionMake(ASDimensionUnitPoints, points); return ASDimensionMake(ASDimensionUnitPoints, points);
} }
@ -122,7 +122,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensio
* Examples: ASDimensionMake(@"50%") = ASDimensionMake(ASDimensionUnitFraction, 0.5) * Examples: ASDimensionMake(@"50%") = ASDimensionMake(ASDimensionUnitFraction, 0.5)
* ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5) * ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5)
*/ */
ASOVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN ASDimension ASDimensionMake(NSString *dimension); AS_OVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN ASDimension ASDimensionMake(NSString *dimension);
/** /**
* Returns a dimension with the specified points value. * Returns a dimension with the specified points value.
@ -244,7 +244,7 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeHasSignificantArea(AS
/** /**
* Creates an ASSizeRange with provided min and max size. * Creates an ASSizeRange with provided min and max size.
*/ */
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max) AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
{ {
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width); ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height); ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
@ -263,7 +263,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRang
/** /**
* Creates an ASSizeRange with provided size as both min and max. * Creates an ASSizeRange with provided size as both min and max.
*/ */
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize) AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize)
{ {
return ASSizeRangeMake(exactSize, exactSize); return ASSizeRangeMake(exactSize, exactSize);
} }

View File

@ -25,7 +25,7 @@
ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0}; ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0};
ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension) AS_OVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
{ {
if (dimension.length > 0) { if (dimension.length > 0) {

View File

@ -25,6 +25,7 @@
#import <AsyncDisplayKit/ASInternalHelpers.h> #import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASSignpost.h> #import <AsyncDisplayKit/ASSignpost.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h> #import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
@interface ASDisplayNode () <_ASDisplayLayerDelegate> @interface ASDisplayNode () <_ASDisplayLayerDelegate>
@ -111,10 +112,13 @@
CGContextTranslateCTM(context, frame.origin.x, frame.origin.y); CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
//support cornerRadius // Support cornerRadius
if (rasterizingFromAscendent && clipsToBounds) { if (rasterizingFromAscendent && clipsToBounds) {
if (cornerRadius) { if (cornerRadius) {
[[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius] addClip]; CGPathRef cornerRadiusPath = ASCGRoundedPathCreate(bounds, cornerRadius);
CGContextAddPath(context, cornerRadiusPath);
CGContextClip(context);
CGPathRelease(cornerRadiusPath);
} else { } else {
CGContextClipToRect(context, bounds); CGContextClipToRect(context, bounds);
} }
@ -127,13 +131,18 @@
CGContextFillRect(context, bounds); CGContextFillRect(context, bounds);
} }
// If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store). // If there is a display block, call it to get the image, then copy the image into the current context (which
// is the rasterized container's backing store).
if (displayBlock) { if (displayBlock) {
UIImage *image = (UIImage *)displayBlock(); UIImage *image = (UIImage *)displayBlock();
if (image) { if (image) {
BOOL opaque = ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage)); BOOL opaque = ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage));
CGBlendMode blendMode = opaque ? kCGBlendModeCopy : kCGBlendModeNormal; CGBlendMode blendMode = opaque ? kCGBlendModeCopy : kCGBlendModeNormal;
[image drawInRect:bounds blendMode:blendMode alpha:1]; CGContextSetBlendMode(context, blendMode);
CGContextTranslateCTM(context, 0, CGRectGetMaxY(bounds) + CGRectGetMinY(bounds));
CGContextScaleCTM(context, 1, -1);
CGContextSetAlpha(context, 1.0);
CGContextDrawImage(context, bounds, image.CGImage);
} }
} }
}; };
@ -295,7 +304,10 @@
ASDisplayNodeAssert(context == UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self); ASDisplayNodeAssert(context == UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
// TODO: This clip path should be removed if we are rasterizing. // TODO: This clip path should be removed if we are rasterizing.
CGRect boundingBox = CGContextGetClipBoundingBox(context); CGRect boundingBox = CGContextGetClipBoundingBox(context);
[[UIBezierPath bezierPathWithRoundedRect:boundingBox cornerRadius:cornerRadius] addClip]; CGPathRef cornerRadiusPath = ASCGRoundedPathCreate(boundingBox, cornerRadius);
CGContextAddPath(context, cornerRadiusPath);
CGContextClip(context);
CGPathRelease(cornerRadiusPath);
} }
if (willDisplayNodeContentWithRenderingContext) { if (willDisplayNodeContentWithRenderingContext) {
@ -332,34 +344,58 @@
CGFloat white = 0.0f, alpha = 0.0f; CGFloat white = 0.0f, alpha = 0.0f;
[backgroundColor getWhite:&white alpha:&alpha]; [backgroundColor getWhite:&white alpha:&alpha];
ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale); ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale);
context = UIGraphicsGetCurrentContext();
[*image drawInRect:bounds]; [*image drawInRect:bounds];
} else { } else {
bounds = CGContextGetClipBoundingBox(context); bounds = CGContextGetClipBoundingBox(context);
} }
ASDisplayNodeAssert(UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self); ASDisplayNodeAssert(UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
CGContextSaveGState(context);
UIBezierPath *roundedHole = [UIBezierPath bezierPathWithRect:bounds]; CGMutablePathRef roundedHole = CGPathCreateMutable();
[roundedHole appendPath:[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius * contentsScale]]; CGPathAddRect(roundedHole, NULL, bounds);
roundedHole.usesEvenOddFillRule = YES;
CGPathRef additionalPath = ASCGRoundedPathCreate(bounds, cornerRadius * contentsScale);
UIBezierPath *roundedPath = nil; CGPathAddPath(roundedHole, NULL, additionalPath);
if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0
CGFloat strokeThickness = borderWidth * contentsScale; CGContextAddPath(context, roundedHole);
CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f;
roundedPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(bounds, strokeInset, strokeInset)
cornerRadius:_cornerRadius * contentsScale];
roundedPath.lineWidth = strokeThickness;
[[UIColor colorWithCGColor:borderColor] setStroke];
}
// Punch out the corners by copying the backgroundColor over them. // Punch out the corners by copying the backgroundColor over them.
// This works for everything from clearColor to opaque colors. // This works for everything from clearColor to opaque colors.
[backgroundColor setFill]; CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
[roundedHole fillWithBlendMode:kCGBlendModeCopy alpha:1.0f];
[roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil. CGContextSetAlpha(context, 1.0);
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextEOFillPath(context);
CGPathRelease(additionalPath);
CGPathRelease(roundedHole);
CGContextRestoreGState(context);
// Drawing borders with ASCornerRoundingTypePrecomposited set has some problems at the moment. If the borderWidth is
// set, besides we are drawing the border with the given corner radius, the CALayer also picks up the borderWidth
// value and draws the border without the cornerRadius.
if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0
CGContextSaveGState(context);
CGFloat strokeThickness = borderWidth * contentsScale;
CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f;
CGPathRef roundedPath = ASCGRoundedPathCreate(CGRectInset(bounds, strokeInset, strokeInset), _cornerRadius * contentsScale);
CGContextAddPath(context, roundedPath);
CGContextSetLineWidth(context, strokeThickness);
CGContextSetStrokeColorWithColor(context, borderColor);
CGContextStrokePath(context);
CGPathRelease(roundedPath);
CGContextRestoreGState(context);
}
if (*image) { if (*image) {
*image = ASGraphicsGetImageAndEndCurrentContext(); *image = ASGraphicsGetImageAndEndCurrentContext();
} }

View File

@ -19,6 +19,7 @@
#import <AsyncDisplayKit/ASGraphicsContext.h> #import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASInternalHelpers.h> #import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASAssert.h> #import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#pragma mark - ASDKFastImageNamed #pragma mark - ASDKFastImageNamed
@ -114,9 +115,9 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
// UIBezierPath objects are fairly small and these are equally sized. 20 should be plenty for many different parameters. // UIBezierPath objects are fairly small and these are equally sized. 20 should be plenty for many different parameters.
__pathCache.countLimit = 20; __pathCache.countLimit = 20;
}); });
// Treat clear background color as no background color // Treat clear background color as no background color
if ([cornerColor isEqual:[UIColor clearColor]]) { if (CGColorGetAlpha(cornerColor.CGColor) == 0) {
cornerColor = nil; cornerColor = nil;
} }
@ -140,33 +141,46 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
// We should probably check if the background color has any alpha component but that // We should probably check if the background color has any alpha component but that
// might be expensive due to needing to check mulitple color spaces. // might be expensive due to needing to check mulitple color spaces.
ASGraphicsBeginImageContextWithOptions(bounds.size, cornerColor != nil, scale); ASGraphicsBeginImageContextWithOptions(bounds.size, cornerColor != nil, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw Corners
BOOL contextIsClean = YES; BOOL contextIsClean = YES;
if (cornerColor) { if (cornerColor) {
contextIsClean = NO; contextIsClean = NO;
[cornerColor setFill];
CGContextSetFillColorWithColor(context, cornerColor.CGColor);
// Copy "blend" mode is extra fast because it disregards any value currently in the buffer and overrides directly. // Copy "blend" mode is extra fast because it disregards any value currently in the buffer and overrides directly.
UIRectFillUsingBlendMode(bounds, kCGBlendModeCopy); CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextFillRect(context, bounds);
} }
// Draw fill
BOOL canUseCopy = contextIsClean || (CGColorGetAlpha(fillColor.CGColor) == 1); BOOL canUseCopy = contextIsClean || (CGColorGetAlpha(fillColor.CGColor) == 1);
[fillColor setFill]; CGContextSetFillColorWithColor(context, fillColor.CGColor);
[path fillWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1]; CGContextSetBlendMode(context, canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal);
CGContextSetAlpha(context, 1.0);
CGContextAddPath(context, path.CGPath);
CGContextFillPath(context);
// Add a border
if (borderColor) { if (borderColor) {
[borderColor setStroke];
// Inset border fully inside filled path (not halfway on each side of path) // Inset border fully inside filled path (not halfway on each side of path)
CGRect strokeRect = CGRectInset(bounds, borderWidth / 2.0, borderWidth / 2.0); CGRect strokeRect = CGRectInset(bounds, borderWidth / 2.0, borderWidth / 2.0);
// It is rarer to have a stroke path, and our cache key only handles rounded rects for the exact-stretchable // It is rarer to have a stroke path, and our cache key only handles rounded rects for the exact-stretchable
// size calculated by cornerRadius, so we won't bother caching this path. Profiling validates this decision. // size calculated by cornerRadius, so we won't bother caching this path. Profiling validates this decision.
UIBezierPath *strokePath = [UIBezierPath bezierPathWithRoundedRect:strokeRect CGPathRef strokePath = ASCGRoundedPathCreate(strokeRect, roundedCorners, cornerRadii);
byRoundingCorners:roundedCorners
cornerRadii:cornerRadii]; CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
[strokePath setLineWidth:borderWidth]; CGContextSetLineWidth(context, borderWidth);
CGContextSetAlpha(context, 1.0);
BOOL canUseCopy = (CGColorGetAlpha(borderColor.CGColor) == 1); BOOL canUseCopy = (CGColorGetAlpha(borderColor.CGColor) == 1);
[strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1]; CGContextSetBlendMode(context, (canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal));
CGContextAddPath(context, strokePath);
CGContextStrokePath(context);
CGPathRelease(strokePath);
} }
UIImage *result = ASGraphicsGetImageAndEndCurrentContext(); UIImage *result = ASGraphicsGetImageAndEndCurrentContext();

View File

@ -83,7 +83,9 @@
layer.shadowColor = [UIColor blackColor].CGColor; layer.shadowColor = [UIColor blackColor].CGColor;
layer.shadowRadius = 12.0; layer.shadowRadius = 12.0;
layer.shadowOpacity = 0.45; layer.shadowOpacity = 0.45;
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);
} }
- (void)setDefaultFocusAppearance - (void)setDefaultFocusAppearance
@ -93,7 +95,9 @@
layer.shadowColor = [UIColor blackColor].CGColor; layer.shadowColor = [UIColor blackColor].CGColor;
layer.shadowRadius = 0; layer.shadowRadius = 0;
layer.shadowOpacity = 0; layer.shadowOpacity = 0;
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
} }
@end @end

View File

@ -169,7 +169,9 @@
layer.shadowColor = [UIColor blackColor].CGColor; layer.shadowColor = [UIColor blackColor].CGColor;
layer.shadowRadius = 12.0; layer.shadowRadius = 12.0;
layer.shadowOpacity = 0.45; layer.shadowOpacity = 0.45;
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25); view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -79,8 +79,8 @@
_photoImageNode.delegate = self; _photoImageNode.delegate = self;
_photoImageNode.URL = photo.URL; _photoImageNode.URL = photo.URL;
_photoImageNode.layerBacked = YES; _photoImageNode.layerBacked = YES;
_userNameLabel = [[ASTextNode alloc] init]; _userNameLabel = [[ASTextNode alloc] init];
_userNameLabel.attributedText = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE]; _userNameLabel.attributedText = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
_photoLocationLabel = [[ASTextNode alloc] init]; _photoLocationLabel = [[ASTextNode alloc] init];