2. - (nullable UIImage *)imageByGrayscale;
该方法的作用就是返回一个返回灰度图像。
方法实现
- (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;
该方法的作用就是对此图像应用模糊效果。 适合模糊任何内容。
方法实现
- (UIImage *)imageByBlurSoft {
return [self imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:0.84 alpha:0.36] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
这里调用的方法与2中调用的方法是一样的,不同的就是传入的参数值不同。
4. - (nullable UIImage *)imageByBlurLight;
该方法的作用就是对此图像应用模糊效果。 适用于模糊除纯白色以外的任何内容。(与iOS控制面板相同)
方法实现
- (UIImage *)imageByBlurLight {
return [self imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:1.0 alpha:0.3] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
}
这里调用的方法与2中调用的方法是一样的,不同的就是传入的参数值不同。
5. - (nullable UIImage *)imageByBlurExtraLight;
该方法的作用就是对此图像应用模糊效果。 适合显示黑色文本,(与iOS导航栏白色相同)。
方法实现
- (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;
该方法的作用就是对此图像应用模糊效果。 适合显示白色文本, (与iOS通知中心相同)
方法实现
- (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;
该方法的作用就是为此图像应用模糊和色调颜色。
方法实现
- (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;
大家对这个方法应该不是很陌生吧,前面很多地方都调用了这个方法。
该方法的作用就是对此图像应用模糊,色调和饱和度调整,可选地在由maskImage指定的区域内。
blurRadius:模糊点的半径,0表示没有模糊效果。
tintColor:与模糊和饱和度操作的结果均匀混合的可选UIColor对象。 这种颜色的alpha通道决定着色的强度。 nil意味着没有色彩。
tintBlendMode:@a tintColor混合模式。 默认是kCGBlendModeNormal(0)
saturation:1.0的值在结果图像中不产生变化。 小于1.0的值会使得到的图像去饱和,而大于1.0的值则会产生相反的效果。 0表示灰度。
maskImage:如果指定,则@a inputImage仅在由此掩码定义的区域中进行修改。 这必须是一个图像掩码,或者它必须满足CGContextClipToMask的掩码参数的要求。
这里具体如何实现就不贴出来了,前面已经贴出来过了。