Add one `colorAtPoint` to help user get the pixel color. Expose the category for `UIColor` and `NSBezierPath` because it can be used in common cases

This commit is contained in:
DreamPiggy 2018-02-19 23:56:08 +08:00
parent 8742e21fab
commit 01402c0369
3 changed files with 199 additions and 45 deletions

View File

@ -21,45 +21,6 @@ NSString * _Nullable SDTransformedKeyForKey(NSString * _Nullable key, NSString *
return [[key stringByAppendingString:SDWebImageTransformerKeySeparator] stringByAppendingString:transformerKey];
}
@interface UIColor (Additions)
@property (nonatomic, copy, readonly, nonnull) NSString *sd_hexString;
@end
@implementation UIColor (Additions)
- (NSString *)sd_hexString {
CGFloat red, green, blue, alpha;
#if SD_UIKIT
if (![self getRed:&red green:&green blue:&blue alpha:&alpha]) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#else
@try {
[self getRed:&red green:&green blue:&blue alpha:&alpha];
}
@catch (NSException *exception) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#endif
red = roundf(red * 255.f);
green = roundf(green * 255.f);
blue = roundf(blue * 255.f);
alpha = roundf(alpha * 255.f);
uint hex = ((uint)alpha << 24) | ((uint)red << 16) | ((uint)green << 8) | ((uint)blue);
return [NSString stringWithFormat:@"0x%08x", hex];
}
@end
@interface SDWebImagePipelineTransformer ()
@property (nonatomic, copy, readwrite, nonnull) NSArray<id<SDWebImageTransformer>> *transformers;

View File

@ -26,6 +26,28 @@ typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
};
#endif
#pragma mark - Useful category
@interface UIColor (Additions)
/**
Convenience way to get hex string from color. The output should always be 32-bit hex string like `0x00000000`
*/
@property (nonatomic, copy, readonly, nonnull) NSString *sd_hexString;
@end
#if SD_MAC
@interface NSBezierPath (Additions)
/**
Convenience way to create a bezier path with the specify rouunding corners on macOS. Same as the one on `UIBezierPath`.
*/
+ (instancetype)sd_bezierPathWithRoundedRect:(NSRect)rect byRoundingCorners:(SDRectCorner)corners cornerRadius:(CGFloat)cornerRadius;
@end
#endif
/**
Provide some commen method for `UIImage`.
Image process is based on Core Graphics and vImage.
@ -101,6 +123,14 @@ typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
*/
- (nullable UIImage *)sd_tintedImageWithColor:(nonnull UIColor *)tintColor;
/**
Return the color at specify pixel. The postion is from the top-left to the bottom-right. And the color is always be RGBA format.
@param point The position of pixel
@return The color for specify pixel, or nil if any error occur
*/
- (nullable UIColor *)sd_colorAtPoint:(CGPoint)point;
#pragma mark - Image Effect
/**

View File

@ -35,9 +35,6 @@ static CGContextRef SDCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque,
static void SDGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) {
#if SD_UIKIT || SD_WATCH
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, -size.height);
CGContextScaleCTM(context, scale, -scale);
#else
CGContextRef context = SDCGContextCreateARGBBitmapContext(size, opaque, scale);
if (!context) {
@ -148,13 +145,41 @@ static CGRect SDCGRectFitWithScaleMode(CGRect rect, CGSize size, SDImageScaleMod
return rect;
}
#if SD_MAC
@interface NSBezierPath (Additions)
@implementation UIColor (Additions)
+ (instancetype)sd_bezierPathWithRoundedRect:(NSRect)rect byRoundingCorners:(SDRectCorner)corners cornerRadius:(CGFloat)cornerRadius;
- (NSString *)sd_hexString {
CGFloat red, green, blue, alpha;
#if SD_UIKIT
if (![self getRed:&red green:&green blue:&blue alpha:&alpha]) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#else
@try {
[self getRed:&red green:&green blue:&blue alpha:&alpha];
}
@catch (NSException *exception) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#endif
red = roundf(red * 255.f);
green = roundf(green * 255.f);
blue = roundf(blue * 255.f);
alpha = roundf(alpha * 255.f);
uint hex = ((uint)alpha << 24) | ((uint)red << 16) | ((uint)green << 8) | ((uint)blue);
return [NSString stringWithFormat:@"0x%08x", hex];
}
@end
#if SD_MAC
@implementation NSBezierPath (Additions)
+ (instancetype)sd_bezierPathWithRoundedRect:(NSRect)rect byRoundingCorners:(SDRectCorner)corners cornerRadius:(CGFloat)cornerRadius {
@ -383,8 +408,146 @@ static CGRect SDCGRectFitWithScaleMode(CGRect rect, CGSize size, SDImageScaleMod
return image;
}
- (UIColor *)sd_colorAtPoint:(CGPoint)point {
if (!self) {
return nil;
}
CGImageRef imageRef = self.CGImage;
if (!imageRef) {
return nil;
}
// Check point
CGFloat width = CGImageGetWidth(imageRef);
CGFloat height = CGImageGetHeight(imageRef);
if (point.x < 0 || point.y < 0 || point.x > width || point.y > height) {
return nil;
}
// Get pixels
CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
if (!provider) {
return nil;
}
CFDataRef data = CGDataProviderCopyData(provider);
if (!data) {
return nil;
}
// Get pixel at point
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef); // Actually should be ARGB8888, equal to width * 4(alpha) or 3(non-alpha)
size_t components = CGImageGetBitsPerPixel(imageRef) / CGImageGetBitsPerComponent(imageRef);
CFRange range = CFRangeMake(bytesPerRow * point.y + components * point.x, 4);
if (CFDataGetLength(data) < range.location + range.length) {
return nil;
}
UInt8 pixel[4] = {0};
CFDataGetBytes(data, range, pixel);
// Convert to color
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGFloat r = 0, g = 0, b = 0, a = 0;
BOOL byteOrderNormal = NO;
switch (bitmapInfo & kCGBitmapByteOrderMask) {
case kCGBitmapByteOrderDefault: {
byteOrderNormal = YES;
} break;
case kCGBitmapByteOrder32Little: {
} break;
case kCGBitmapByteOrder32Big: {
byteOrderNormal = YES;
} break;
default: break;
}
switch (alphaInfo) {
case kCGImageAlphaPremultipliedFirst: {
if (byteOrderNormal) {
// ARGB8888
a = pixel[0] / 255.0;
r = pixel[1] / 255.0;
g = pixel[2] / 255.0;
b = pixel[3] / 255.0;
} else {
// BGRA8888
b = pixel[0] / 255.0;
g = pixel[1] / 255.0;
r = pixel[2] / 255.0;
a = pixel[3] / 255.0;
}
}
break;
case kCGImageAlphaPremultipliedLast: {
if (byteOrderNormal) {
// RGBA8888
r = pixel[0] / 255.0;
g = pixel[1] / 255.0;
b = pixel[2] / 255.0;
a = pixel[3] / 255.0;
} else {
// ABGR8888
a = pixel[0] / 255.0;
b = pixel[1] / 255.0;
g = pixel[2] / 255.0;
r = pixel[3] / 255.0;
}
}
break;
case kCGImageAlphaNone: {
if (byteOrderNormal) {
// RGB
r = pixel[0] / 255.0;
g = pixel[1] / 255.0;
b = pixel[2] / 255.0;
} else {
// BGR
b = pixel[0] / 255.0;
g = pixel[1] / 255.0;
r = pixel[2] / 255.0;
}
}
break;
case kCGImageAlphaNoneSkipLast: {
if (byteOrderNormal) {
// RGBX
r = pixel[0] / 255.0;
g = pixel[1] / 255.0;
b = pixel[2] / 255.0;
} else {
// XBGR
b = pixel[1] / 255.0;
g = pixel[2] / 255.0;
r = pixel[3] / 255.0;
}
}
break;
case kCGImageAlphaNoneSkipFirst: {
if (byteOrderNormal) {
// XRGB
r = pixel[1] / 255.0;
g = pixel[2] / 255.0;
b = pixel[3] / 255.0;
} else {
// BGRX
b = pixel[0] / 255.0;
g = pixel[1] / 255.0;
r = pixel[2] / 255.0;
}
}
break;
// iOS does not supports non-premultiplied alpha, so no these cases :)
default:
break;
}
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
#pragma mark - Image Effect
// We use vImage to do box convolve for performance. However, you can just use `CIFilter.CIBoxBlur`. For other blur effect, use any filter in `CICategoryBlur`
- (UIImage *)sd_blurredImageWithRadius:(CGFloat)blurRadius {
if (self.size.width < 1 || self.size.height < 1) {
return nil;