版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2018.03.25 |
前言
iOS圈內(nèi)有幾個(gè)人大家基本都知道,比如說王巍教硫、唐巧瞬矩,還有YYKit框架的作者現(xiàn)任職于滴滴的郭曜源 - ibireme等景用。這里有一篇唐巧對他的專訪惭蹂,還有他的 GitHub - Yaoyuan 和 博客盾碗,這里貼出來框架YYKit 框架置尔。接下來幾篇我們就一起來看一下這個(gè)框架榜轿。感興趣的可以看上面寫的幾篇谬盐。
1. YYKit源碼探究(一) —— 基本概覽
2. YYKit源碼探究(二) —— NSString分類之Hash(一)
3. YYKit源碼探究(三) —— NSString分類之Encode and decode(二)
4. YYKit源碼探究(四) —— NSString分類之Drawing(三)
5. YYKit源碼探究(五) —— NSString分類之Regular Expression(四)
6. YYKit源碼探究(六) —— NSString分類之NSNumber Compatible(五)
7. YYKit源碼探究(七) —— NSString分類之Utilities(六)
8. YYKit源碼探究(八) —— NSNumber分類(一)
9. YYKit源碼探究(九) —— UIFont分類之架構(gòu)分析和Font Traits(一)
10. YYKit源碼探究(十) —— UIFont分類之Create font(二)
11. YYKit源碼探究(十一) —— UIFont分類之Load and unload font(三)
12. YYKit源碼探究(十二) —— UIFont分類之Dump font data(四)
13. YYKit源碼探究(十三) —— UIImage分類之框架結(jié)構(gòu)和Create image部分(一)
14. YYKit源碼探究(十四) —— UIImage分類之Image Info(二)
15. YYKit源碼探究(十五) —— UIImage分類之Modify Image(三)
回顧
上一篇主要介紹了UIImage分類的Modify Image
,這一篇我們就看一下Image Effect
部分弃鸦。
API
下面看一下API唬格。
/**
Tint the image in alpha channel with the given color.
@param color The color.
*/
- (nullable UIImage *)imageByTintColor:(UIColor *)color;
/**
Returns a grayscaled image.
*/
- (nullable UIImage *)imageByGrayscale;
/**
Applies a blur effect to this image. Suitable for blur any content.
*/
- (nullable UIImage *)imageByBlurSoft;
/**
Applies a blur effect to this image. Suitable for blur any content except pure white.
(same as iOS Control Panel)
*/
- (nullable UIImage *)imageByBlurLight;
/**
Applies a blur effect to this image. Suitable for displaying black text.
(same as iOS Navigation Bar White)
*/
- (nullable UIImage *)imageByBlurExtraLight;
/**
Applies a blur effect to this image. Suitable for displaying white text.
(same as iOS Notification Center)
*/
- (nullable UIImage *)imageByBlurDark;
/**
Applies a blur and tint color to this image.
@param tintColor The tint color.
*/
- (nullable UIImage *)imageByBlurWithTint:(UIColor *)tintColor;
/**
Applies a blur, tint color, and saturation adjustment to this image,
optionally within the area specified by @a maskImage.
@param blurRadius The radius of the blur in points, 0 means no blur effect.
@param tintColor An optional UIColor object that is uniformly blended with
the result of the blur and saturation operations. The
alpha channel of this color determines how strong the
tint is. nil means no tint.
@param tintBlendMode The @a tintColor blend mode. Default is kCGBlendModeNormal (0).
@param saturation A value of 1.0 produces no change in the resulting image.
Values less than 1.0 will desaturation the resulting image
while values greater than 1.0 will have the opposite effect.
0 means gray scale.
@param maskImage If specified, @a inputImage is only modified in the area(s)
defined by this mask. This must be an image mask or it
must meet the requirements of the mask parameter of
CGContextClipToMask.
@return image with effect, or nil if an error occurs (e.g. no
enough memory).
*/
- (nullable UIImage *)imageByBlurRadius:(CGFloat)blurRadius
tintColor:(nullable UIColor *)tintColor
tintMode:(CGBlendMode)tintBlendMode
saturation:(CGFloat)saturation
maskImage:(nullable UIImage *)maskImage;
1. - (nullable UIImage *)imageByTintColor:(UIColor *)color;
該方法的作用就是用給定顏色在alpha通道中對圖像著色。
方法實(shí)現(xiàn)
- (UIImage *)imageByTintColor:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
[color set];
UIRectFill(rect);
[self drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeDestinationIn alpha:1];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
2. - (nullable UIImage *)imageByGrayscale;
該方法的作用就是返回一個(gè)返回灰度圖像烹困。
方法實(shí)現(xiàn)
- (UIImage *)imageByGrayscale {
return [self imageByBlurRadius:0 tintColor:nil tintMode:0 saturation:0 maskImage:nil];
}
- (UIImage *)imageByBlurRadius:(CGFloat)blurRadius
tintColor:(UIColor *)tintColor
tintMode:(CGBlendMode)tintBlendMode
saturation:(CGFloat)saturation
maskImage:(UIImage *)maskImage {
if (self.size.width < 1 || self.size.height < 1) {
NSLog(@"UIImage+YYAdd error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
return nil;
}
if (!self.CGImage) {
NSLog(@"UIImage+YYAdd error: inputImage must be backed by a CGImage: %@", self);
return nil;
}
if (maskImage && !maskImage.CGImage) {
NSLog(@"UIImage+YYAdd error: effectMaskImage must be backed by a CGImage: %@", maskImage);
return nil;
}
// iOS7 and above can use new func.
BOOL hasNewFunc = (long)vImageBuffer_InitWithCGImage != 0 && (long)vImageCreateCGImageFromBuffer != 0;
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
BOOL hasSaturation = fabs(saturation - 1.0) > __FLT_EPSILON__;
CGSize size = self.size;
CGRect rect = { CGPointZero, size };
CGFloat scale = self.scale;
CGImageRef imageRef = self.CGImage;
BOOL opaque = NO;
if (!hasBlur && !hasSaturation) {
return [self _yy_mergeImageRef:imageRef tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
}
vImage_Buffer effect = { 0 }, scratch = { 0 };
vImage_Buffer *input = NULL, *output = NULL;
vImage_CGImageFormat format = {
.bitsPerComponent = 8,
.bitsPerPixel = 32,
.colorSpace = NULL,
.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, //requests a BGRA buffer.
.version = 0,
.decode = NULL,
.renderingIntent = kCGRenderingIntentDefault
};
if (hasNewFunc) {
vImage_Error err;
err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImagePrintDiagnosticsToConsole);
if (err != kvImageNoError) {
NSLog(@"UIImage+YYAdd error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self);
return nil;
}
err = vImageBuffer_Init(&scratch, effect.height, effect.width, format.bitsPerPixel, kvImageNoFlags);
if (err != kvImageNoError) {
NSLog(@"UIImage+YYAdd error: vImageBuffer_Init returned error code %zi for inputImage: %@", err, self);
return nil;
}
} else {
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef effectCtx = UIGraphicsGetCurrentContext();
CGContextScaleCTM(effectCtx, 1.0, -1.0);
CGContextTranslateCTM(effectCtx, 0, -size.height);
CGContextDrawImage(effectCtx, rect, imageRef);
effect.data = CGBitmapContextGetData(effectCtx);
effect.width = CGBitmapContextGetWidth(effectCtx);
effect.height = CGBitmapContextGetHeight(effectCtx);
effect.rowBytes = CGBitmapContextGetBytesPerRow(effectCtx);
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef scratchCtx = UIGraphicsGetCurrentContext();
scratch.data = CGBitmapContextGetData(scratchCtx);
scratch.width = CGBitmapContextGetWidth(scratchCtx);
scratch.height = CGBitmapContextGetHeight(scratchCtx);
scratch.rowBytes = CGBitmapContextGetBytesPerRow(scratchCtx);
}
input = &effect;
output = &scratch;
if (hasBlur) {
// A description of how to compute the box kernel width from the Gaussian
// radius (aka standard deviation) appears in the SVG spec:
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
//
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
// successive box-blurs build a piece-wise quadratic convolution kernel, which
// approximates the Gaussian kernel to within roughly 3%.
//
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
//
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
//
CGFloat inputRadius = blurRadius * scale;
if (inputRadius - 2.0 < __FLT_EPSILON__) inputRadius = 2.0;
uint32_t radius = floor((inputRadius * 3.0 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
radius |= 1; // force radius to be odd so that the three box-blur methodology works.
int iterations;
if (blurRadius * scale < 0.5) iterations = 1;
else if (blurRadius * scale < 1.5) iterations = 2;
else iterations = 3;
NSInteger tempSize = vImageBoxConvolve_ARGB8888(input, output, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
void *temp = malloc(tempSize);
for (int i = 0; i < iterations; i++) {
vImageBoxConvolve_ARGB8888(input, output, temp, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
YY_SWAP(input, output);
}
free(temp);
}
if (hasSaturation) {
// These values appear in the W3C Filter Effects spec:
// https://dvcs.w3.org/hg/FXTF/raw-file/default/filters/Publish.html#grayscaleEquivalent
CGFloat s = saturation;
CGFloat matrixFloat[] = {
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
0, 0, 0, 1,
};
const int32_t divisor = 256;
NSUInteger matrixSize = sizeof(matrixFloat) / sizeof(matrixFloat[0]);
int16_t matrix[matrixSize];
for (NSUInteger i = 0; i < matrixSize; ++i) {
matrix[i] = (int16_t)roundf(matrixFloat[i] * divisor);
}
vImageMatrixMultiply_ARGB8888(input, output, matrix, divisor, NULL, NULL, kvImageNoFlags);
YY_SWAP(input, output);
}
UIImage *outputImage = nil;
if (hasNewFunc) {
CGImageRef effectCGImage = NULL;
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, &_yy_cleanupBuffer, NULL, kvImageNoAllocate, NULL);
if (effectCGImage == NULL) {
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, NULL, NULL, kvImageNoFlags, NULL);
free(input->data);
}
free(output->data);
outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
CGImageRelease(effectCGImage);
} else {
CGImageRef effectCGImage;
UIImage *effectImage;
if (input != &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (input == &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
effectCGImage = effectImage.CGImage;
outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
}
return outputImage;
}
// Helper function to add tint and mask.
- (UIImage *)_yy_mergeImageRef:(CGImageRef)effectCGImage
tintColor:(UIColor *)tintColor
tintBlendMode:(CGBlendMode)tintBlendMode
maskImage:(UIImage *)maskImage
opaque:(BOOL)opaque {
BOOL hasTint = tintColor != nil && CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__;
BOOL hasMask = maskImage != nil;
CGSize size = self.size;
CGRect rect = { CGPointZero, size };
CGFloat scale = self.scale;
if (!hasTint && !hasMask) {
return [UIImage imageWithCGImage:effectCGImage];
}
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -size.height);
if (hasMask) {
CGContextDrawImage(context, rect, self.CGImage);
CGContextSaveGState(context);
CGContextClipToMask(context, rect, maskImage.CGImage);
}
CGContextDrawImage(context, rect, effectCGImage);
if (hasTint) {
CGContextSaveGState(context);
CGContextSetBlendMode(context, tintBlendMode);
CGContextSetFillColorWithColor(context, tintColor.CGColor);
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
}
if (hasMask) {
CGContextRestoreGState(context);
}
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
3. - (nullable UIImage *)imageByBlurSoft;
該方法的作用就是對此圖像應(yīng)用模糊效果屈张。 適合模糊任何內(nèi)容阁谆。
方法實(shí)現(xiàn)
- (UIImage *)imageByBlurSoft {
return [self imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:0.84 alpha:0.36] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
這里調(diào)用的方法與2中調(diào)用的方法是一樣的场绿,不同的就是傳入的參數(shù)值不同。
4. - (nullable UIImage *)imageByBlurLight;
該方法的作用就是對此圖像應(yīng)用模糊效果璧尸。 適用于模糊除純白色以外的任何內(nèi)容爷光。(與iOS控制面板相同)
方法實(shí)現(xiàn)
- (UIImage *)imageByBlurLight {
return [self imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:1.0 alpha:0.3] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
這里調(diào)用的方法與2中調(diào)用的方法是一樣的蛀序,不同的就是傳入的參數(shù)值不同徐裸。
5. - (nullable UIImage *)imageByBlurExtraLight;
該方法的作用就是對此圖像應(yīng)用模糊效果啸盏。 適合顯示黑色文本重贺,(與iOS導(dǎo)航欄白色相同)。
方法實(shí)現(xiàn)
- (UIImage *)imageByBlurExtraLight {
return [self imageByBlurRadius:40 tintColor:[UIColor colorWithWhite:0.97 alpha:0.82] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
6. - (nullable UIImage *)imageByBlurDark;
該方法的作用就是對此圖像應(yīng)用模糊效果。 適合顯示白色文本檬姥, (與iOS通知中心相同)
方法實(shí)現(xiàn)
- (UIImage *)imageByBlurDark {
return [self imageByBlurRadius:40 tintColor:[UIColor colorWithWhite:0.11 alpha:0.73] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
7. - (nullable UIImage *)imageByBlurWithTint:(UIColor *)tintColor;
該方法的作用就是為此圖像應(yīng)用模糊和色調(diào)顏色曾我。
方法實(shí)現(xiàn)
- (UIImage *)imageByBlurWithTint:(UIColor *)tintColor {
const CGFloat EffectColorAlpha = 0.6;
UIColor *effectColor = tintColor;
size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
if (componentCount == 2) {
CGFloat b;
if ([tintColor getWhite:&b alpha:NULL]) {
effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
}
} else {
CGFloat r, g, b;
if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
}
}
return [self imageByBlurRadius:20 tintColor:effectColor tintMode:kCGBlendModeNormal saturation:-1.0 maskImage:nil];
}
8. - (nullable UIImage *)imageByBlurRadius:(CGFloat)blurRadius tintColor:(nullable UIColor *)tintColor tintMode:(CGBlendMode)tintBlendMode saturation:(CGFloat)saturation maskImage:(nullable UIImage *)maskImage;
大家對這個(gè)方法應(yīng)該不是很陌生吧,前面很多地方都調(diào)用了這個(gè)方法健民。
該方法的作用就是對此圖像應(yīng)用模糊抒巢,色調(diào)和飽和度調(diào)整,可選地在由maskImage
指定的區(qū)域內(nèi)蛉谜。
-
blurRadius
:模糊點(diǎn)的半徑,0表示沒有模糊效果崇堵。 -
tintColor
:與模糊和飽和度操作的結(jié)果均勻混合的可選UIColor對象型诚。 這種顏色的alpha通道決定著色的強(qiáng)度。 nil意味著沒有色彩鸳劳。 -
tintBlendMode
:@a tintColor
混合模式狰贯。 默認(rèn)是kCGBlendModeNormal(0)
-
saturation
:1.0的值在結(jié)果圖像中不產(chǎn)生變化。 小于1.0的值會(huì)使得到的圖像去飽和赏廓,而大于1.0的值則會(huì)產(chǎn)生相反的效果涵紊。 0表示灰度。 -
maskImage
:如果指定幔摸,則@a inputImage
僅在由此掩碼定義的區(qū)域中進(jìn)行修改摸柄。 這必須是一個(gè)圖像掩碼,或者它必須滿足CGContextClipToMask
的掩碼參數(shù)的要求既忆。
這里具體如何實(shí)現(xiàn)就不貼出來了驱负,前面已經(jīng)貼出來過了。
后記
本篇主要介紹了UIImage分類的
Image Effect
患雇,感興趣的給個(gè)贊或者關(guān)注跃脊,謝謝~~~