mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
178 lines
5.2 KiB
Plaintext
178 lines
5.2 KiB
Plaintext
//
|
|
// ASTextKitShadower.mm
|
|
// Texture
|
|
//
|
|
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
|
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
|
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
|
|
#import "ASTextKitShadower.h"
|
|
|
|
#if AS_ENABLE_TEXTNODE
|
|
|
|
#import <tgmath.h>
|
|
|
|
static inline CGSize _insetSize(CGSize size, UIEdgeInsets insets)
|
|
{
|
|
size.width -= (insets.left + insets.right);
|
|
size.height -= (insets.top + insets.bottom);
|
|
return size;
|
|
}
|
|
|
|
static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets)
|
|
{
|
|
return {
|
|
.top = -insets.top,
|
|
.left = -insets.left,
|
|
.bottom = -insets.bottom,
|
|
.right = -insets.right
|
|
};
|
|
}
|
|
|
|
@implementation ASTextKitShadower {
|
|
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
|
|
shadowRadius:(CGFloat)shadowRadius
|
|
{
|
|
if (self = [super init]) {
|
|
_shadowOffset = shadowOffset;
|
|
_shadowColor = shadowColor;
|
|
_shadowOpacity = shadowOpacity;
|
|
_shadowRadius = shadowRadius;
|
|
_calculatedShadowPadding = UIEdgeInsetsMake(-INFINITY, -INFINITY, INFINITY, INFINITY);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
* This method is duplicated here because it gets called frequently, and we were
|
|
* wasting valuable time constructing a state object to ask it.
|
|
*/
|
|
- (BOOL)_shouldDrawShadow
|
|
{
|
|
return _shadowOpacity != 0.0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor.CGColor) > 0;
|
|
}
|
|
|
|
- (void)setShadowInContext:(CGContextRef)context
|
|
{
|
|
if ([self _shouldDrawShadow]) {
|
|
CGColorRef textShadowColor = CGColorRetain(_shadowColor.CGColor);
|
|
CGSize textShadowOffset = _shadowOffset;
|
|
CGFloat textShadowOpacity = _shadowOpacity;
|
|
CGFloat textShadowRadius = _shadowRadius;
|
|
|
|
if (textShadowOpacity != 1.0) {
|
|
CGFloat inherentAlpha = CGColorGetAlpha(textShadowColor);
|
|
|
|
CGColorRef oldTextShadowColor = textShadowColor;
|
|
textShadowColor = CGColorCreateCopyWithAlpha(textShadowColor, inherentAlpha * textShadowOpacity);
|
|
CGColorRelease(oldTextShadowColor);
|
|
}
|
|
|
|
CGContextSetShadowWithColor(context, textShadowOffset, textShadowRadius, textShadowColor);
|
|
|
|
CGColorRelease(textShadowColor);
|
|
}
|
|
}
|
|
|
|
|
|
- (UIEdgeInsets)shadowPadding
|
|
{
|
|
if (_calculatedShadowPadding.top == -INFINITY) {
|
|
if (![self _shouldDrawShadow]) {
|
|
return UIEdgeInsetsZero;
|
|
}
|
|
|
|
UIEdgeInsets shadowPadding = UIEdgeInsetsZero;
|
|
|
|
// min values are expected to be negative for most typical shadowOffset and
|
|
// blurRadius settings:
|
|
shadowPadding.top = std::fmin(0.0f, _shadowOffset.height - _shadowRadius);
|
|
shadowPadding.left = std::fmin(0.0f, _shadowOffset.width - _shadowRadius);
|
|
|
|
shadowPadding.bottom = std::fmin(0.0f, -_shadowOffset.height - _shadowRadius);
|
|
shadowPadding.right = std::fmin(0.0f, -_shadowOffset.width - _shadowRadius);
|
|
|
|
_calculatedShadowPadding = shadowPadding;
|
|
}
|
|
|
|
return _calculatedShadowPadding;
|
|
}
|
|
|
|
- (CGSize)insetSizeWithConstrainedSize:(CGSize)constrainedSize
|
|
{
|
|
return _insetSize(constrainedSize, _invertInsets([self shadowPadding]));
|
|
}
|
|
|
|
- (CGRect)insetRectWithConstrainedRect:(CGRect)constrainedRect
|
|
{
|
|
return UIEdgeInsetsInsetRect(constrainedRect, _invertInsets([self shadowPadding]));
|
|
}
|
|
|
|
- (CGSize)outsetSizeWithInsetSize:(CGSize)insetSize
|
|
{
|
|
return _insetSize(insetSize, [self shadowPadding]);
|
|
}
|
|
|
|
- (CGRect)outsetRectWithInsetRect:(CGRect)insetRect
|
|
{
|
|
return UIEdgeInsetsInsetRect(insetRect, [self shadowPadding]);
|
|
}
|
|
|
|
- (CGRect)offsetRectWithInternalRect:(CGRect)internalRect
|
|
{
|
|
return (CGRect){
|
|
.origin = [self offsetPointWithInternalPoint:internalRect.origin],
|
|
.size = internalRect.size
|
|
};
|
|
}
|
|
|
|
- (CGPoint)offsetPointWithInternalPoint:(CGPoint)internalPoint
|
|
{
|
|
UIEdgeInsets shadowPadding = [self shadowPadding];
|
|
return (CGPoint){
|
|
internalPoint.x + shadowPadding.left,
|
|
internalPoint.y + shadowPadding.top
|
|
};
|
|
}
|
|
|
|
- (CGPoint)offsetPointWithExternalPoint:(CGPoint)externalPoint
|
|
{
|
|
UIEdgeInsets shadowPadding = [self shadowPadding];
|
|
return (CGPoint){
|
|
externalPoint.x - shadowPadding.left,
|
|
externalPoint.y - shadowPadding.top
|
|
};
|
|
}
|
|
|
|
@end
|
|
|
|
#endif
|