iOS图形图像及骨干动画实战二Block基础知识,二维码的变迁澳门网上正规赌场网址

本教程是一个合集,涉及到的目录结构:基础知识总结Block基础知识GCD实战CoreGraphics
& ImageIO实战CoreAnimation实战

本文译自 objc.io出品的书籍《Functional Programming in
swift》第三章,objc.io 由 Chris Eidhof, Daniel Eggert 和 Florian
Kugler 成立于柏林。成 objc.io 的目的是针对深入的、跟所有 iOS 和 OS X
开发者相关的技术话题创造一个正式的平台。objc .io
出过24期的期刊,每一期都针对特定的主题提出解决方案,
可以到objc中国查看这些文章的中文版本。本书延续了 objc.io
一贯的风格,讲解得很深入,可惜译者水平有限,无法将书中的精彩之处忠实的表达出来。
想购买正版书籍请到

1.二维码

a. 二维码的本质
二维码的本质就是一个 NSString 类型的字符串.
/*

  • 二维码的内容是字符串
  • CoreImage中有个核心, CIFilter滤镜
  • CIFilter的参数设置是通过KVC来配置, 而不是属性
  • CoreImage框架提供了很多可以直接使用的滤镜
  • 一个滤镜的输出可以是另一个滤镜的输入
    (多个滤镜的效果可以组合起来使用)
  • 二维码的内容越多, 黑黑的方格就越多
    根据字符串来生成一个对应的二维码
    /
    b. 如何生成二维码

    1. 首先要导入一个专门用来处理静态与动态图片的框架#import
      <CoreImage/CoreImage.h>
      ,这里的一个关键类叫做滤镜
      CIFilter
      ,它的作用就相当于一个染坊
      ,为图片添加特殊的图片效果的从而生成一张新的图片
      2.iOS
      系统为开发者提供了许多不同功能的滤镜
      ,其中比如有渲染颜色的,有二维码的等等,我们可以从对应CIFilter.h
      头文件中找到
      . 废话不多讲了,直接贴代码:
      /**
  • 二维码的内容是字符串
  • CoreImage中有个核心, CIFilter滤镜
  • CIFilter的参数设置是通过KVC来配置, 而不是属性
  • CoreImage框架提供了很多可以直接使用的滤镜

根据字符串来生成一个对应的二维码
*/

Block是在iOS
4.0及以上系统才提供的一种类似于匿名函数的语法,它被定义在SDK中,所以基本上现在所有的开发人员都是不得不需要掌握的一项技能。

前一章介绍了高阶函数的概念,并演示了怎样将函数做为一个参数传递给另一个函数。然而,这个例子可能与你日常编写的真实的代码关联不大。在这一章中,我将像你演示怎样使用高阶函数对已有的面向对象的API编写一个小型的,函数式的封装。

import “ViewController.h”

// 专门用来处理静态与动态图片的处理 (实现一些特殊效果)

本篇文章也仅仅作为快速入门使用,不涉及底层实现,如对Block有所了解,请移步!

Core Image是一个非常强大的图形处理库,但是它的API难于使用。Core
Image的API是弱类型的–图形滤镜使用key-value编码进行配置。这使得很容易弄错参数的类型或名字,着可以编译通过,但会发生运行时错误。我们基于Core
Image开发的新的API是类型安全的,模块化的,可以避免这种运行时的错误。

import <CoreImage/CoreImage.h>

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

/* 滤镜 (为图片添加特殊效果 -> 生成新的图片) */
@property (strong, nonatomic) CIFilter *filter;

@end

@implementation ViewController

  • (void)viewDidLoad
    {
    [super viewDidLoad];

    /**

    • 获取所有可用的滤镜 (名字)
      kCICategoryBuiltIn 指是系统内建的滤镜
      */
      NSArray <NSString *> *fileNames = [CIFilter
      filterNamesInCategory:kCICategoryBuiltIn];
      NSLog(@”fileNames: %@”, fileNames);

    /**

    • 必须要确保滤镜名字是正确, CIQRCodeGenerator
      */
      // 根据滤镜名创建对应的滤镜 (滤镜名必须正确, 而且是系统提供的)
      self.filter = [CIFilter
      filterWithName:@”CIQRCodeGenerator”];
      // 1. 通过文档来查找该滤镜 (图文效果)
      // 2. 通过代码方式来查找, 找到相应的Key与Value的描述
      NSDictionary <NSString *, id> *attributes =
      self.filter.attributes;
      NSLog(@”%@”, attributes);

    // 直接生成一张字符串对应的二维码图片, 并不需要图片输入

    // ——– 配置输入参数 ——–
    NSString *message = @”科比一路走好, 我永远怀念你!
    死在朋友圈里了!”;
    NSData *data = [message
    dataUsingEncoding:NSUTF8StringEncoding];
    [self.filter setValue:data forKey:@”inputMessage”];

    // 会影响二维码能被遮挡的内容大小 (H接近30%)
    [self.filter setValue:@”H” forKey:@”inputCorrectionLevel”];

    // ——– 获取 ——–
    // 获取滤镜的输出图片
    CIImage *outputImage = self.filter.outputImage;
    UIImage *image = [UIImage imageWithCIImage:outputImage];

    self.imageView.image = image;
    }

@end

c.展示效果

澳门网上正规赌场网址 1

image.png

d. 生成彩色二维码

/**
 * 必须要确保滤镜名字是正确, CIQRCodeGenerator
 */
NSArray <NSString *> *fileNames = [CIFilter filterNamesInCategory:kCICategoryBuiltIn];
NSLog(@"fileNames: %@", fileNames);
// 根据滤镜名创建对应的滤镜 (滤镜名必须正确, 而且是系统提供的)
self.filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// 1. 通过文档来查找该滤镜 (图文效果)
// 2. 通过代码方式来查找, 找到相应的Key与Value的描述
NSDictionary <NSString *, id> *attributes = self.filter.attributes;
NSLog(@"%@", attributes);

// 直接生成一张字符串对应的二维码图片, 并不需要图片输入

// -------- 配置输入参数 --------
NSString *message = @"OurAppName://RequestFriend?username=yueyue!";
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
[self.filter setValue:data forKey:@"inputMessage"];

// 会影响二维码能被遮挡的内容大小 (H接近30%)
[self.filter setValue:@"H" forKey:@"inputCorrectionLevel"];

// -------- 获取 --------
// 获取滤镜的输出图片, outputImage是二维码滤镜输出的二维码图片
CIImage *outputImage = self.filter.outputImage;

// -------- 修改颜色 --------
CIFilter *filter = [CIFilter filterWithName:@"CIFalseColor"];

// 配置参数 (参考文档)
[filter setValue:outputImage forKey:@"inputImage"];

CIColor *color0 = [CIColor colorWithRed:10/255.0 green:110/255.0 blue:180/255.0];
CIColor *color1 = [CIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0];
[filter setValue:color0 forKey:@"inputColor0"];
[filter setValue:color1 forKey:@"inputColor1"];

outputImage = filter.outputImage;

// -------- 调整CIImage的大小 --------
outputImage = [outputImage imageByApplyingTransform:CGAffineTransformMakeScale(20, 20)];
NSLog(@"Size: %@", NSStringFromCGSize(outputImage.extent.size));

UIImage *image = [UIImage imageWithCIImage:outputImage];

self.imageView.image = image;  


E *展示效果*  

澳门网上正规赌场网址 2

image.png

F. 生成带头像的二维码    

Block的基础结构剖析

先上张图:

澳门网上正规赌场网址 3Paste_Image.png

这张图应该很经典了,基本上不用Google,百度这个渣渣都能搜索到。

Block定义部分主要了解几个地方即可:

  • 符号中文叫:脱字符,定义Block时必须要写,也可以理解为有符号才应该是Block
  • Block可以有返回值,也就是图上最左边的int
  • Block有个名字,也就是图上的myBlock
  • Block可以带参数,也就是图上的从左往右第二个int

Block实现部分主要了解几个地方即可:

  • 以脱字符打头
  • 括号内是参数
  • 使用花括号包起来,内部写实际的算法代码,然后该返回就返回(例如图上应该返回int)

如果你对Core
Image不熟悉,看不懂本章中编写的一些代码片段,也不用担心。本章的目的并不是编写一个完整的基于CoreImage的封装库,而只是为了说明函数式编程像高阶函数这样的概念是怎样应用在生产代码中的。如果你对OC语言和NSDictionary的使用不熟悉,你可以在第一次通读的时候跳过本章,然后回过头来再学习它。

import “UIImage+QRCode.h”

@implementation UIImage (QRCode)
*- (UIImage *)imageWithIcon:(UIImage *)icon
{
// 图形上下文
UIGraphicsBeginImageContext(self.size);

// 将原来的图片画上去 (二维码)
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];

// 将用户头像画上去, 假定用户头像的宽高是原图25%
CGFloat wh = MIN(self.size.width, self.size.height) * 0.25;
CGFloat centerX = (self.size.width - wh) / 2;
CGFloat centerY = (self.size.height - wh) / 2;
[icon drawInRect:CGRectMake(centerX, centerY, wh, wh)];

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return newImage;

}

@end

Block实战

  • 最最有代表性的用法

int result = myBlock;printf("%d",result);

这段代码会输出:70

  • 如果Block就仅仅上面这点用,那就太小看它了,我们来点最最实在的 —
    链式语法

链式语法白话理解就是先产生某个对象,没个函数操作实际又会返回它自己(其实也可以返回它自己类型的指针),然后一路
. 下去,不断的可以调用函数,最后一个函数即可得到一个最终结果。

例如下面代码,就是一路 . 下去:

maker.add.subtract.multiply.divide;

最终的结果为:6以上计算步骤为:0 + 10 = 1010 – 2 = 88 * 3 = 2424 / 4 =
6

哈哈哈,帅不帅!

我们剖析链式语法实现之前先来了解个东西:

  • property中用Block

@property (nonatomic,readonly) CaculatorMaker *(NSInteger number);@property (nonatomic,readonly) CaculatorMaker *(^subtract)(NSInteger number);@property (nonatomic,readonly) CaculatorMaker *(^multiply)(NSInteger number);@property (nonatomic,readonly) CaculatorMaker *(NSInteger number);
  • method中用Block

- (CaculatorMaker* (NSInteger))add;- (CaculatorMaker* (NSInteger))subtract;- (CaculatorMaker* (NSInteger))multiply;- (CaculatorMaker* (NSInteger))divide;

Property中用Block,我想应该很容易理解,但是那个Method中用Block初学者很容易看蒙。蒙在哪呢,主要在add前面那个NSIneteger,其实这个是代表add的参数!😓

仔细剖析下:

澳门网上正规赌场网址 4Method中用Block

a -> 代表静态函数b -> Block返回值、参数打头括号及结尾括号c ->
返回值d -> 表明这是一个Blocke -> 参数f -> 函数命名

OK,准备工作都已做好,我们来看看链式语法的实操!

CoreImage是Apple封装好的一个滤镜操作库,它可以让我们很容易的对图像进行滤镜。

来来来,我们利用前面的Block基础知识以及链式语法的基础来实现一个最基本的棕色滤镜:

@interface CIImage // 棕色滤镜@property (nonatomic, readonly) CIImage *(^sepiaTone)(CGFloat intensity);// 拉直滤镜@property (nonatomic, readonly) CIImage *(^straightenFilter)(CGFloat angle);

- (CIImage* (CGFloat intensity))sepiaTone { return ^CIImage* (CGFloat intensity) { CIFilter *sepiaFilter = [CIFilter filterWithName:@"CISepiaTone"]; [sepiaFilter setValue:self forKey:kCIInputImageKey]; [sepiaFilter setValue:@(intensity) forKey:kCIInputIntensityKey]; return [sepiaFilter outputImage]; };}- (CIImage*(CGFloat angle))straightenFilter { return ^(CGFloat angle) { CIFilter *filter = [CIFilter filterWithName:@"CIStraightenFilter"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:@ forKey:kCIInputAngleKey]; return filter.outputImage; };}

然后我们来测试下看看:

CIImage *ciimage = [CIImage imageWithPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"1_1.png"]];UIImage *image = ciimage.sepiaTone.straightenFilter.UIImage;self.imageView.image = image;

澳门网上正规赌场网址 5原图澳门网上正规赌场网址 6滤镜后

啦啦啦,啦啦啦,我能装逼了~~~哈哈哈

言归正传,Block是一个浅入深出的技术,它最基本的用处是匿名函数,但是同时它如果配合其他的模式就能产生无穷大的力量!

滤镜类型(The Filter Type)

CoreImage关键的一个类是用于对图片进行滤镜处理的CIFilter类,当你初始化CIFilter类时,你需要通过kCIInputImageKey键提供一个输入图片(input
image)并通过kCIOutputImageKey键取得滤镜处理后的图片。然后你就可以使用这个图片做进一步的处理。

本章要开发的API中,我们会尝试封装这些键值对(key-value pairs
)的详细信息并提供一个安全的,强类型的API给用户。我们创建我们自己的Filter类型,这个Filter类型是一个函数,它使用一张图片做为参数并返回一张新的滤镜处理过的图片:

typealias Filter = CIImage -> CIImage

这就是将要在其之上建立宏伟大厦的基础类型。

备注

CoreImage其实底层提供了非常多的滤镜,但是Apple没有很直接的暴露出来,只提供了一个获取滤镜的函数:[CIFilter
filterNamesInCategory:kCICategoryBuiltIn]每个滤镜也可获取它的输入、输出参数:CIFilter
filter = [CIFilter filterWithName:name];NSArray<NSString>
inputKeys = filter.inputKeys;NSArray<NSString> *outputKeys =
filter.outputKeys;

#import <Foundation/Foundation.h>#import <CoreImage/CoreImage.h>#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGIN@interface CIImage // 棕色滤镜@property (nonatomic, readonly) CIImage *(^sepiaTone)(CGFloat intensity);// 仿射变换滤镜@property (nonatomic, readonly) CIImage *(^affineTransform)(CGAffineTransform transform);// 混合滤镜(给有Alpha通道的图片添加底层图片)@property (nonatomic, readonly) CIImage *(^sourceAtopCompositing)(CIImage *backgroundImage);// 拉直滤镜@property (nonatomic, readonly) CIImage *(^straightenFilter)(CGFloat angle);// 色彩控制滤镜@property (nonatomic, readonly) CIImage *(^colorControls)(CGFloat bright,CGFloat contrast,CGFloat saturation);// 反转颜色滤镜@property (nonatomic,readonly) CIImage *(^colorInvert);@property (nonatomic,readonly)CIImage *(^accordionFoldTransition)(CIImage *targetImage,CGFloat bottomHeight,NSInteger numberOfFolds,NSInteger foldShadowAmount,CGFloat time);@property (nonatomic,readonly)CIImage *(^additionCompositing)(CIImage *backgroundImage);@property (nonatomic,readonly)CIImage *(^affineClamp)(CGAffineTransform transform);@property (nonatomic,readonly)CIImage *(^affineTile)(CGAffineTransform transform);//TODO: 下面的滤镜还未实现@property (nonatomic,readonly)CIImage *(^areaHistogram)(NSString *extent,NSString *scale,NSString *count);@property (nonatomic,readonly)CIImage *(^aztecCodeGenerator)(NSString *message,NSString *correctionLevel,NSString *layers,NSString *compactStyle);@property (nonatomic,readonly)CIImage *(^barsSwipeTransition)(NSString *targetImage,NSString *angle,NSString *width,NSString *barOffset,NSString *time);@property (nonatomic,readonly)CIImage *(^blendWithAlphaMask)(NSString *backgroundImage,NSString *maskImage);@property (nonatomic,readonly)CIImage *(^blendWithMask)(NSString *backgroundImage,NSString *maskImage);@property (nonatomic,readonly)CIImage *(NSString *radius,NSString *intensity);@property (nonatomic,readonly)CIImage *(^bumpDistortion)(NSString *center,NSString *radius,NSString *scale);@property (nonatomic,readonly)CIImage *(^bumpDistortionLinear)(NSString *center,NSString *radius,NSString *angle,NSString *scale);@property (nonatomic,readonly)CIImage *(^checkerboardGenerator)(NSString *center,NSString *color0,NSString *color1,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^circleSplashDistortion)(NSString *center,NSString *radius);@property (nonatomic,readonly)CIImage *(^circularScreen)(NSString *center,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^code128BarcodeGenerator)(NSString *message,NSString *quietSpace);@property (nonatomic,readonly)CIImage *(^colorBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^colorBurnBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^colorClamp)(NSString *minComponents,NSString *maxComponents);@property (nonatomic,readonly)CIImage *(^colorCrossPolynomial)(NSString *coefficients,NSString *redCoefficients,NSString *greenCoefficients,NSString *blueCoefficients);@property (nonatomic,readonly)CIImage *(^colorCube)(NSString *cubeDimension,NSString *cubeData);@property (nonatomic,readonly)CIImage *(^colorCubeWithColorSpace)(NSString *cubeDimension,NSString *cubeData,NSString *colorSpace);@property (nonatomic,readonly)CIImage *(^colorDodgeBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^colorMap)(NSString *gradientImage);@property (nonatomic,readonly)CIImage *(^colorMatrix)(NSString *rVector,NSString *gVector,NSString *bVector,NSString *aVector,NSString *biasVector);@property (nonatomic,readonly)CIImage *(^colorMonochrome)(NSString *color,NSString *intensity);@property (nonatomic,readonly)CIImage *(^colorPolynomial)(NSString *redCoefficients,NSString *greenCoefficients,NSString *blueCoefficients,NSString *alphaCoefficients);@property (nonatomic,readonly)CIImage *(^colorPosterize)(NSString *levels);@property (nonatomic,readonly)CIImage *(^constantColorGenerator)(NSString *color);@property (nonatomic,readonly)CIImage *(^convolution3X3)(NSString *weights,NSString *bias);@property (nonatomic,readonly)CIImage *(^convolution5X5)(NSString *weights,NSString *bias);@property (nonatomic,readonly)CIImage *(^convolution9Horizontal)(NSString *weights,NSString *bias);@property (nonatomic,readonly)CIImage *(^convolution9Vertical)(NSString *weights,NSString *bias);@property (nonatomic,readonly)CIImage *(^copyMachineTransition)(NSString *targetImage,NSString *extent,NSString *color,NSString *time,NSString *angle,NSString *width,NSString *opacity);@property (nonatomic,readonly)CIImage *(NSString *rectangle);@property (nonatomic,readonly)CIImage *(^darkenBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^differenceBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^disintegrateWithMaskTransition)(NSString *targetImage,NSString *maskImage,NSString *time,NSString *shadowRadius,NSString *shadowDensity,NSString *shadowOffset);@property (nonatomic,readonly)CIImage *(^dissolveTransition)(NSString *targetImage,NSString *time);@property (nonatomic,readonly)CIImage *(^divideBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^dotScreen)(NSString *center,NSString *angle,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^eightfoldReflectedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(^exclusionBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^exposureAdjust)(NSString *eV);@property (nonatomic,readonly)CIImage *(^falseColor)(NSString *color0,NSString *color1);@property (nonatomic,readonly)CIImage *(^flashTransition)(NSString *targetImage,NSString *center,NSString *extent,NSString *color,NSString *time,NSString *maxStriationRadius,NSString *striationStrength,NSString *striationContrast,NSString *fadeThreshold);@property (nonatomic,readonly)CIImage *(^fourfoldReflectedTile)(NSString *center,NSString *angle,NSString *width,NSString *acuteAngle);@property (nonatomic,readonly)CIImage *(^fourfoldRotatedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(^fourfoldTranslatedTile)(NSString *center,NSString *angle,NSString *width,NSString *acuteAngle);@property (nonatomic,readonly)CIImage *(^gammaAdjust)(NSString *power);@property (nonatomic,readonly)CIImage *(^gaussianBlur)(NSString *radius);@property (nonatomic,readonly)CIImage *(^gaussianGradient)(NSString *center,NSString *color0,NSString *color1,NSString *radius);@property (nonatomic,readonly)CIImage *(^glassDistortion)(NSString *texture,NSString *center,NSString *scale);@property (nonatomic,readonly)CIImage *(^glideReflectedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(NSString *radius,NSString *intensity);@property (nonatomic,readonly)CIImage *(^hardLightBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^hatchedScreen)(NSString *center,NSString *angle,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^highlightShadowAdjust)(NSString *radius,NSString *shadowAmount,NSString *highlightAmount);@property (nonatomic,readonly)CIImage *(^histogramDisplayFilter)(NSString *height,NSString *highLimit,NSString *lowLimit);@property (nonatomic,readonly)CIImage *(^holeDistortion)(NSString *center,NSString *radius);@property (nonatomic,readonly)CIImage *(^hueAdjust)(NSString *angle);@property (nonatomic,readonly)CIImage *(^hueBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^lanczosScaleTransform)(NSString *scale,NSString *aspectRatio);@property (nonatomic,readonly)CIImage *(^lightenBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^lightTunnel)(NSString *center,NSString *rotation,NSString *radius);@property (nonatomic,readonly)CIImage *(^linearBurnBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^linearDodgeBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^linearGradient)(NSString *point0,NSString *point1,NSString *color0,NSString *color1);@property (nonatomic,readonly)CIImage *(^linearToSRGBToneCurve);@property (nonatomic,readonly)CIImage *(^lineScreen)(NSString *center,NSString *angle,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^luminosityBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^maskToAlpha);@property (nonatomic,readonly)CIImage *(^maximumComponent);@property (nonatomic,readonly)CIImage *(^maximumCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^minimumComponent);@property (nonatomic,readonly)CIImage *(^minimumCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^modTransition)(NSString *targetImage,NSString *center,NSString *time,NSString *angle,NSString *radius,NSString *compression);@property (nonatomic,readonly)CIImage *(^motionBlur)(NSString *radius,NSString *angle);@property (nonatomic,readonly)CIImage *(^multiplyBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^multiplyCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^overlayBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^perspectiveCorrection)(NSString *topLeft,NSString *topRight,NSString *bottomRight,NSString *bottomLeft);@property (nonatomic,readonly)CIImage *(^photoEffectChrome);@property (nonatomic,readonly)CIImage *(^photoEffectFade);@property (nonatomic,readonly)CIImage *(^photoEffectInstant);@property (nonatomic,readonly)CIImage *(^photoEffectMono);@property (nonatomic,readonly)CIImage *(^photoEffectNoir);@property (nonatomic,readonly)CIImage *(^photoEffectProcess);@property (nonatomic,readonly)CIImage *(^photoEffectTonal);@property (nonatomic,readonly)CIImage *(^photoEffectTransfer);@property (nonatomic,readonly)CIImage *(^pinchDistortion)(NSString *center,NSString *radius,NSString *scale);@property (nonatomic,readonly)CIImage *(^pinLightBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^pixellate)(NSString *center,NSString *scale);@property (nonatomic,readonly)CIImage *(^qRCodeGenerator)(NSString *message,NSString *correctionLevel);@property (nonatomic,readonly)CIImage *(^radialGradient)(NSString *center,NSString *radius0,NSString *radius1,NSString *color0,NSString *color1);@property (nonatomic,readonly)CIImage *(^randomGenerator);@property (nonatomic,readonly)CIImage *(^saturationBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^screenBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^sharpenLuminance)(NSString *sharpness);@property (nonatomic,readonly)CIImage *(^sixfoldReflectedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(^sixfoldRotatedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(^smoothLinearGradient)(NSString *point0,NSString *point1,NSString *color0,NSString *color1);@property (nonatomic,readonly)CIImage *(^softLightBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^sourceInCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^sourceOutCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^sourceOverCompositing)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^sRGBToneCurveToLinear);@property (nonatomic,readonly)CIImage *(^starShineGenerator)(NSString *center,NSString *color,NSString *radius,NSString *crossScale,NSString *crossAngle,NSString *crossOpacity,NSString *crossWidth,NSString *epsilon);@property (nonatomic,readonly)CIImage *(^stripesGenerator)(NSString *center,NSString *color0,NSString *color1,NSString *width,NSString *sharpness);@property (nonatomic,readonly)CIImage *(^subtractBlendMode)(NSString *backgroundImage);@property (nonatomic,readonly)CIImage *(^swipeTransition)(NSString *targetImage,NSString *extent,NSString *color,NSString *time,NSString *angle,NSString *width,NSString *opacity);@property (nonatomic,readonly)CIImage *(^temperatureAndTint)(NSString *neutral,NSString *targetNeutral);@property (nonatomic,readonly)CIImage *(^toneCurve)(NSString *point0,NSString *point1,NSString *point2,NSString *point3,NSString *point4);@property (nonatomic,readonly)CIImage *(^triangleKaleidoscope)(NSString *point,NSString *size,NSString *rotation,NSString *decay);@property (nonatomic,readonly)CIImage *(^twelvefoldReflectedTile)(NSString *center,NSString *angle,NSString *width);@property (nonatomic,readonly)CIImage *(^twirlDistortion)(NSString *center,NSString *radius,NSString *angle);@property (nonatomic,readonly)CIImage *(^unsharpMask)(NSString *radius,NSString *intensity);@property (nonatomic,readonly)CIImage *(^vibrance)(NSString *amount);@property (nonatomic,readonly)CIImage *(^vignette)(NSString *intensity,NSString *radius);@property (nonatomic,readonly)CIImage *(^vignetteEffect)(NSString *center,NSString *radius,NSString *intensity,NSString *falloff);@property (nonatomic,readonly)CIImage *(^vortexDistortion)(NSString *center,NSString *radius,NSString *angle);@property (nonatomic,readonly)CIImage *(^whitePointAdjust)(NSString *color);@property (nonatomic,readonly)CIImage *(^zoomBlur)(NSString *radius,NSString *center);/** * @author huangxinping, 16-04-01 * * 获取所有可用滤镜 * * @return 滤镜名称 */+ (NSArray<NSString*>*)filterNames;/** * @author huangxinping * * 从路径创建 * * @param path 路径 * * @return 实例 */+ (nullable instancetype)imageWithPath:(nonnull NSString*)path;/** * @author huangxinping, 16-04-01 * * 转换到UIImage * * @return UIImage */- (nullable UIImage*)UIImage;/** * @author huangxinping, 16-04-01 * * 是否是面部 * * @return YES-是面部;NO-不是面部 */- hasFace;/** * @author huangxinping, 16-04-01 * * 检测有多少个特征(CIDetectorTypeFace、CIDetectorTypeRectangle、CIDetectorTypeQRCode、CIDetectorTypeText) * * @return feature数组 */- (nullable NSArray<CIFeature*>*)featuresWithType:(nonnull NSString*)type;/** * @author huangxinping, 16-04-01 * * 左眼位置 * * @return 位置数组(NSValue里面是CGPoint) */- (nullable NSArray<NSValue*>*)leftEyePosition;/** * @author huangxinping, 16-04-01 * * 右眼位置 * * @return 位置数组(NSValue里面是CGPoint) */- (nullable NSArray<NSValue*>*)rightEyePosition;/** * @author huangxinping, 16-04-01 * * 嘴巴位置 * * @return 位置数组(NSValue里面是CGPoint) */- (nullable NSArray<NSValue*>*)mouthEyePosition;@endNS_ASSUME_NONNULL_END

#import "CIImage+XPImage.h"@implementation CIImage - (CIImage* (CGFloat intensity))sepiaTone { return ^CIImage* (CGFloat intensity) { CIFilter *sepiaFilter = [CIFilter filterWithName:@"CISepiaTone"]; [sepiaFilter setValue:self forKey:kCIInputImageKey]; [sepiaFilter setValue:@(intensity) forKey:kCIInputIntensityKey]; return [sepiaFilter outputImage]; };}- (CIImage*(CGAffineTransform transform))affineTransform { return ^(CGAffineTransform transform) { CIFilter *transFilter = [CIFilter filterWithName:@"CIAffineTransform"]; [transFilter setValue:self forKey:kCIInputImageKey]; [transFilter setValue:[NSValue valueWithCGAffineTransform:transform] forKey:kCIInputTransformKey]; return transFilter.outputImage; };}- (CIImage*(CIImage *backgroundImage))sourceAtopCompositing { return ^(CIImage *backgroundImage) { CIFilter *filter = [CIFilter filterWithName:@"CISourceAtopCompositing"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:backgroundImage forKey:kCIInputBackgroundImageKey]; return filter.outputImage; };}- (CIImage*(CGFloat angle))straightenFilter { return ^(CGFloat angle) { CIFilter *filter = [CIFilter filterWithName:@"CIStraightenFilter"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:@ forKey:kCIInputAngleKey]; return filter.outputImage; };}- (CIImage*(CGFloat bright,CGFloat contrast,CGFloat saturation))colorControls { return ^(CGFloat bright,CGFloat contrast,CGFloat saturation) { CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:@ forKey:kCIInputBrightnessKey]; [filter setValue:@ forKey:kCIInputContrastKey]; [filter setValue:@(saturation) forKey:kCIInputSaturationKey]; return filter.outputImage; };}- (CIImage*colorInvert { return ^{ CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; [filter setValue:self forKey:kCIInputImageKey]; return filter.outputImage; };}- (CIImage*(CIImage *targetImage,CGFloat bottomHeight,NSInteger numberOfFolds,NSInteger foldShadowAmount,CGFloat time))accordionFoldTransition { return ^(CIImage *targetImage,CGFloat bottomHeight,NSInteger numberOfFolds,NSInteger foldShadowAmount,CGFloat time){ CIFilter *filter = [CIFilter filterWithName:@"CIAccordionFoldTransition"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:targetImage forKey:kCIInputTargetImageKey]; [filter setValue:@(bottomHeight) forKey:@"inputBottomHeight"]; [filter setValue:@(numberOfFolds) forKey:@"inputNumberOfFolds"]; [filter setValue:@(foldShadowAmount) forKey:@"inputFoldShadowAmount"]; [filter setValue:@ forKey:@"inputTime"]; return filter.outputImage; };}- (CIImage*(CIImage *backgroundImage))additionCompositing { return ^(CIImage *backgroundImage) { CIFilter *filter = [CIFilter filterWithName:@"CIAdditionCompositing"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:backgroundImage forKey:kCIInputBackgroundImageKey]; return filter.outputImage; };}- (CIImage*(CGAffineTransform transform))affineClamp { return ^(CGAffineTransform transform) { CIFilter *filter = [CIFilter filterWithName:@"CIAffineClamp"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:[NSValue valueWithCGAffineTransform:transform] forKey:kCIInputTransformKey]; return filter.outputImage; };}- (CIImage*(CGAffineTransform transform))affineTile { return ^(CGAffineTransform transform) { CIFilter *filter = [CIFilter filterWithName:@"CIAffineTile"]; [filter setValue:self forKey:kCIInputImageKey]; [filter setValue:[NSValue valueWithCGAffineTransform:transform] forKey:kCIInputTransformKey]; return filter.outputImage; };}+ (NSArray<NSString*>*)filterNames { return [CIFilter filterNamesInCategory:kCICategoryBuiltIn];}+ (instancetype)imageWithPath:(NSString*)path { NSURL *url = [NSURL fileURLWithPath:path]; CIImage *coreImage = [CIImage imageWithContentsOfURL:url]; return coreImage;}- UIImage {// return [UIImage imageWithCIImage:self]; // 该函数每一次都会创建一个CIContext对象,对于频繁调用时会很消耗性能。代码如下: // EAGLContext *glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];// if (!glContext) {// NSLog(@"Failed to create ES context");// }// CIContext *context = [CIContext contextWithEAGLContext:glContext]; // 使用GPU// CGImageRef cgimg = [context createCGImage:self fromRect:[self extent]];// UIImage *image = [UIImage imageWithCGImage:cgimg];// CGImageRelease;// return image; // 为了提高性能,我们可以在init时,将CIContext就初始化好,后面直接使用 static CIContext *context = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ EAGLContext *glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!glContext) { NSLog(@"Failed to create ES context"); } context = [CIContext contextWithEAGLContext:glContext]; // 使用GPU }); CGImageRef cgimg = [context createCGImage:self fromRect:[self extent]]; UIImage *image = [UIImage imageWithCGImage:cgimg]; CGImageRelease; return image;}- hasFace { return [self featuresWithType:CIDetectorTypeFace].count?YES:NO;}- (NSArray<CIFeature*>*)featuresWithType:(NSString *)type { CIDetector *faceDetector = [CIDetector detectorOfType:type context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; CIImage *ciimg = [CIImage imageWithCGImage:[self UIImage].CGImage]; NSArray<CIFeature*> *features = [faceDetector featuresInImage:ciimg]; return features;}- leftEyePosition { if (![self hasFace]) { return nil; } NSArray *features = [self featuresWithType:CIDetectorTypeFace]; NSMutableArray *buffer = [NSMutableArray arrayWithCapacity:features.count]; for (CIFaceFeature *f in features) { if (f.hasMouthPosition) { [buffer addObject:[NSValue valueWithCGPoint:f.leftEyePosition]]; } } return buffer;}- rightEyePosition { if (![self hasFace]) { return nil; } NSArray *features = [self featuresWithType:CIDetectorTypeFace]; NSMutableArray *buffer = [NSMutableArray arrayWithCapacity:features.count]; for (CIFaceFeature *f in features) { if (f.hasMouthPosition) { [buffer addObject:[NSValue valueWithCGPoint:f.rightEyePosition]]; } } return buffer;}- (NSArray<NSValue*>*)mouthEyePosition { if (![self hasFace]) { return nil; } NSArray *features = [self featuresWithType:CIDetectorTypeFace]; NSMutableArray *buffer = [NSMutableArray arrayWithCapacity:features.count]; for (CIFaceFeature *f in features) { if (f.hasMouthPosition) { [buffer addObject:[NSValue valueWithCGPoint:f.mouthPosition]]; } } return buffer;}@end

唯一遗憾:本人只把CoreImage中所有滤镜全部定义了出来,实在太懒没写全函数实现,如有大神全部写全,请分享于我!

创建滤镜(Building Filters)

现在我们已经定义了FIlter类型,可以开始定义创建特定滤镜效果的函数了。会定义一组使用特定滤镜的键值参数做为参数的便利函数来构建Filter值。这些函数具有下面通用的形式:

func myFilter(/* parameters */) -> Filter

注意它的返回值,Filter,也是一个函数。后面我们会使用这一特性结合多个滤镜以获得我们想要的滤镜效果。

为了简化代码,我们扩展CIFilter类,添加一个便利构造器以及一个用来获取输出图片的计算属性;

typealias Parameters = Dictionary<String, AnyObject>extension CIFilter { convenience init(name: String, parameters: Parameters) { self.init(name: name) setDefaults() for (key, value: AnyObject) in parameters { setValue(value, forKey: key) } } var outputImage: CIImage { return self.valueForKey(kCIOutputImageKey) as CIImage } }

便利构造器使用滤镜的名字以及一个dictionary做为参数。这个dictionary中的键值对将会设为新滤镜类型的参数。我们编写的便利构造器遵循swift规范调用了指定构造器。

计算属性outputImage提供了从滤镜对象中获取输出图片的便捷方式。它从kCIOutputImageKey键中国年取出内容并将其转换为CIImage对象。通过提供这个计算属性,用户就不需要自己进行这种查找以及转换工作了。

模糊效果

有了这些准备,可以定义第一个滤镜类型了。高斯模糊滤镜只使用一个半径做为它的参数:

func blur(radius: Double) -> Filter { return { image in let parameters: Parameters = [ kCIInputRadiusKey: radius, kCIInputImageKey: image ] let filter = CIFilter(name: "CIGaussianBlur", parameters:parameters) return filter.outputImage } }

这就是模糊滤镜所有的内容了。blur函数返回一个函数,该函数以一个CIImage对象image为参数,并返回一个CIImage类型的对象(return
filter.outputImage)。正因此,这个返回值符合前面所示Filter类型的定义(CIImage
-> CIImage)。

这个函数只是对Core
Image库已有滤镜的简单封装,我们可以多次使用同样的方法创建自己的滤镜函数。

颜色蒙板(Color Overlay)

让我们定义一个在一张图片上覆盖指定的颜色的滤镜。Core
Image默认并不存在这样一个滤镜,但是,我们能够使用已有的滤镜构建出这个滤镜。

我们使用CoreImage的CIConstantColorGenerator滤镜和CISourceOverCompositing滤镜来构建这个滤镜。首先我们定义一个用来生成颜色板的滤镜:

func colorGenerator(color: NSColor) -> Filter { return { _ in let parameters: Parameters = [kCIInputColorKey: color] let filter = CIFilter(name:"CIConstantColorGenerator", parameters: parameters) return filter.outputImage } }

这个函数和我们前面定义的blur函数类似,只有一点不同:CIConstantColorGenerator滤镜并不需要输入图片(input
image),因此,在返回的那个函数中,我们不需要对image参数进行命名。相反,我们使用匿名参数,_,表明在我们定义的这个Filter中会忽略image参数。

下面,我们定义混合滤镜(CISourceOverCompositing):

func compositeSourceOver(overlay: CIImage) -> Filter { return { image in let parameters: Parameters = [ kCIInputBackgroundImageKey: image, kCIInputImageKey: overlay ] let filter = CIFilter(name: "CISourceOverCompositing", parameters: parameters) let cropRect = image.extent() return filter.outputImage.imageByCroppingToRect }}

这里,我们剪裁输出图片,使得它的尺寸和输入图片一样。这并不是必不可少的步骤,取决于我们想让滤镜执行怎样的行为。然而,在我们介绍的例子中进行剪裁是符合要求的。

最后,我们将两个滤镜结合起来创建颜色蒙板滤镜:

func colorOverlay(color: NSColor) -> Filter { return { image in let overlay = colorGenerator return compositeSourceOver }}

再一次,我们返回一个以image作为参数并返回一个image的函数。colorOverlay滤镜首先调用colorGenerator滤镜。colorGenerator滤镜需要一个颜色作为参数并返回一个Filter,因此代码片段colorGenerator返回一个Filter,而这个Filter本身又是一个CIImage->CIImage的函数,我们传递附加的(之所以说是附加的,因为这个参数实际上没有用到)CIImage类型的参数image到colorGenerator,这样执行后会得到一个CIImage类型的对象,然后将这个对象赋值给overlay常量(所以overlay应该是CIImage类型的),这和overlay定义中发生的几乎一样:我们使用colorGenerator函数创建了一个Filter并传递image参数到钙Filter以创建一个新的图片。同样的,返回值compositeSourceOver,compositeSourceOver返回一个Filter,然后将image作为参数传递给这个滤镜,执行后生成一个CIImage类型的对象并返回。

组合滤镜(Composing Filters)

现在我们已经定义了一个blur滤镜以及一个overlay滤镜,我们可以使用结合的方式将之应用到真实的图片上去:首先我们对图片进行模糊处理,然后我们在其上覆盖一个红色蒙板。首先载入一张图片:

let url = NSURL(string: "http://tinyurl.com/m74sldb");let image = CIImage(contentsOfURL: url)

现在我们可以依次使用滤镜进行处理了:

let blurRadius = 5.0let overlayColor = NSColor.redColor().colorWithAlphaComponent let blurredImage = blur(blurRadius)let overlaidImage = colorOverlay(overlayColor)(blurredImage)

再说一遍,要处理一张图片,首先通过诸如blur(blurRadius)的函数创建一个滤镜,然后将图片作为参数传递给该滤镜进行处理,这样就生成一张处理后的CIImage图片。

函数组合(Function Composition )

但让,我们可以简单的组合上面代码中的两个滤镜到一条表达式中:

let result = colorOverlay(overlayColor)(blur(blurRadius)

然而,这样做会让代码的可读性降低,因为需要使用这么多的括号。更好的方式是定义一个自定义函数composeFilters,这个函数可以将两个滤镜组合成一个滤镜。如下:

func composeFilters(filter1: Filter, filter2: Filter) -> Filter { return { img in filter2(filter1 }}

composeFilters函数使用两个Filter作为参数,并返回一个新的Filter。组合滤镜使用CIImage类型的img作为参数,依次将img传递给filter1和filter2,我们可以使用composeFilters函数创建我们自己的混合滤镜,如下:

let myFilter1 = composeFilters(blur(blurRadius), colorOverlay(overlayColor))let result1 = myFilter1

我们可以通过定义一个自定义组合运算符让组合操作更易读。一般情况下,自定义操作赋并不能使得代码更易读,但是,filter组合不同,
filter组合是图片处理库的一个循环任务,因此可以增加可读性:

infix operator >>> { associativity left }func >>> (filter1: Filter, filter2: Filter) -> Filter { return { img in filter2(filter1 }}

现在,我们可以像使用composeFilters函数一样使用>>>运算符:

let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)let result2 = myFilter2

因为我们定义的>>>
运算符具有左结合性,我们可以从左向右依次读取作用在image上的滤镜–就像Unix管道一样

我们定义的滤镜组合运算符是函数组合(function composition,
组合函数)的一个例子。在数学中,两个函数f和g的组合,使用f ◦
g表示,定义了一个新的函数y=f,除了顺序不同外,组合函数和>>>
运算符执行了相同的操作:它传递一个image参数依次通过两个滤镜。

理论背景:柯里化(Theoretical Background: Currying)

本章中,我们看到有两种方法定义拥有两个参数的函数,第一种方式是大多数程序猿都熟悉的方式:

func add1(x: Int, y: Int) -> Int { return x + y}

add1函数使用两个整数作为参数并返回它们的和。然而,使用swift语言,我们还可以定义相同函数的另一个版本:

func add2 -> (Int -> Int) { return { y in return x + y }}

这里,add2函数有一个参数x,并返回一个需要第二个参数y的必报。这两个函数的调用方式也不相同:

add1 add2

第一种情况下,我们同时将两个参数传递给add1函数。第二种情况,我们首先传递第一个参数,1,运算结束后会返回一个函数,然后我们向这个函数传递第二个参数2。两个版本的函数是等价的。

使用Swift语言我们可以省略其中一些return以及一些括号:

func add2 -> Int -> Int { return { y in x + y }}

函数箭头 ->具有右结合性,也就是说,A -> B -> C代表的意思是A
-> (B ->
C)。然而,在本书中,为了良好的可读性,我们会使用别名代表函数类型,或者显式的使用括号代表其顺序。

add1和add2的例子掩饰了怎么把有多个参数的函数转换成只有一个参数的一系列的函数,这个过程称作“柯里化”,以逻辑学家Haskell
Curry的名字命名,所以我们可以说add2是函数add1柯里化版本。

Swift语言还有另外一种柯里化函数的方式。我们不使用在add2方法中构建闭包的那种做法,而是使用如下的方法定义函数add1的柯里化版本:

func add3 -> Int { return x + y}

可以看到,我们一个接一个的列出了add3函数所需要的参数,每个参数都包裹在它自己的括号里面。然而,要调用add3,我们必须显式的提供第二个参数的名字,如下:

add3

为什么需要对函数进行柯里化?我们已经在前面的章节中看到,有一种常见的场景是,你需要传递一个函数作为另一个函数的参数。如果我们使用未经过柯里化的函数版本,比如add1,我们必须一次提供所有的两个参数。然而,使用柯里化的版本,比如add2,我们可以选择提供一个参数或者提供两个参数。本章中我们定义的几个创建滤镜的函数都已经被柯里化了—它们都是用一个image作为参数。以这种方式编写滤镜,我们就可以很容易地使用>>>
运算符将它们组合在一起。如果我们使用的是同一函数未经过柯里化的版本,我们仍然可以编写出一样的滤镜以及滤镜组合运算符,但是写出来的代码可能更笨重一些

讨论(Discussion)

我们举出的例子展示了怎样使用函数式编程将复杂的代码分解成小的,可组装的代码。本章的目标并不是在CoreImage之上封装一层完整的API,而是为了勾勒出怎么将高阶函数以及函数组合应用于实际的开发中

为什么要做这样的努力?确实,CoreImage提供的API已经很成熟并且能够提供你所需的所有功能。但是除此之外,本章我们设计的API还有如下几个优点:

  • 安全 —
    使用本章编写的API,我们几乎不会遇到运行时错误或者强制类型转换失败的错误
  • 模块化—使用>>>操作符组合滤镜很方便。这样做可以让你将复杂的滤镜分离为小型的,简单的,可重用的模块。另外,组合滤镜和它的构建块(building
    blocks) 具有相通的类型,因此是可以互相替换的。
  • 明确—即使你从来没有接触过CoreImage,你也可以使用我们提供的API装配简单的滤镜。要获得滤镜最终的处理结果,你不需要知道dictionary的那些键值对,不需要知道kCIOutputImageKey,也不需要知道初始化的那些键,如kCIInputImageKey,kCIInputRadiusKey。只是根据类型,你就能知道如何使用这个API,即使没有相关的说明文档

我们的API提供了一系列的函数,可以用于定义或者组合滤镜。你定义的所有滤镜都是安全并可以重用的。每个滤镜都可以单独测试和使用。我相信这些就是我们需要在Core
Image之上再封装一层接口的原因。

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注