接下来介绍如何继承一个 CIFilter,实现自定义效果。在继承CIFilter前先介绍两个内置的filter——Face Detecting 和 Auto Enhancing Images。

Image中探测人脸
Core Image 可以在图片中分析和找到人脸——人脸探测,不是人脸识别(能认出是谁)。人脸检测是对包含人脸特征的矩形的识别,而人脸识别则是对特定人脸的识别(能认出是谁)。Core Image 探测到人脸后,他和一提供面部功能的信息,比如眼睛和嘴的位置。也可以在视频中追踪标识人脸位置。
Core Image标识出图片中人脸的范围。
知道人脸在图片的什么位置能让你执行其他操作,比如裁切和调整脸部质量(tone balance, red-eye correction and so on)。也可以执行其他有趣的操作比如:
如何将pixellate filter应用到图像中的人脸。
如何给面部加入虚化。
检测面部
使用 CIDetector 在图片中识别面部,代码如下:
CIContext *context = [CIContext context];                    // 1
NSDictionary *opts = @{ CIDetectorAccuracy : CIDetectorAccuracyHigh };      // 2
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
                                          context:context
                                          options:opts];                    // 3

 

opts = @{ CIDetectorImageOrientation :
          [[myImage properties] valueForKey:kCGImagePropertyOrientation] }; // 4
NSArray *features = [detector featuresInImage:myImage options:opts];        // 5
  1. 用默认选项创建一个CIContext。也可以使用 context的创建函数。创建detector是也可以提供context为nil
  2. 为detector创建一个options 字典容器。你可以选择低或高精度的。低精度(CIDetectorAccuracyLow)快,高精度精确但是慢。
  3. 为面部创建detector。目前智能创建人脸的detector
  4. 为查找人脸设置option字典。让Core Image知道图片是横屏还是竖屏,这样Core Image就能找到正确角度的人脸。大部分时间这个角度是从源图直接获得的,然后提供给option 字典。
  5. 使用detector在图片中找。图片对象必须是一个CIImage对象。Core Image返回一个CIFeature对象数组,每个对象表示一个图片中的人脸。
得到人脸数组后,你可能需要知道他们的特征,比如眼睛和嘴巴的位置。下面介绍方法。
得到面部和面部特征范围
面部特征包括:
  • 左右眼睛位置
  • 嘴巴位置
  • tracking ID和tracking frame数量,Core Image使用这些信息在一段视频中追踪人脸(iOS 6.0和 OS X 10.8以上)
从CIDetector拿到特征数组后,你可以迭代array来验证每个面部范围和每个面部里的其他特征。如下:
for (CIFaceFeature *f in features) {
    NSLog(@”%@”, NSStringFromRect(f.bounds));

 

    if (f.hasLeftEyePosition) {
        NSLog(@”Left eye %g %g”, f.leftEyePosition.x, f.leftEyePosition.y);
    }
    if (f.hasRightEyePosition) {
        NSLog(@”Right eye %g %g”, f.rightEyePosition.x, f.rightEyePosition.y);
    }
    if (f.hasMouthPosition) {
        NSLog(@”Mouth %g %g”, f.mouthPosition.x, f.mouthPosition.y);
    }
}
自动图片增强
Core Image的自动增强特性分析了其直方图、人脸区域内容和元数据属性的图像。返回CIFilter对象数组,这些对象的输入参数已经自动设置为能够改善分析图像的值。需要iOS5和OS X 10.8.
自动增强filter
Core Image使用的自动增益filter。
Filter
Purpose
CIRedEyeCorrection
修红眼Repairs red/amber/white eye due to camera flash
CIFaceBalance
修肤色Adjusts the color of a face to give pleasing skin tones
CIVibrance
虚化Increases the saturation of an image without distorting the skin tones
CIToneCurve
色调Adjusts image contrast
CIHighlightShadowAdjust
修阴影Adjusts shadow details
使用自动增益filter
自动增强API只有两种方法:autoAdjustmentFilters和autoAdjustmentFiltersWithOptions :. 在大多数情况下,你需要使用提供option 字典的方法。
你可以设置这些选项:
  •      图像方向对于CIRedEyeCorrection和CIFaceBalance滤镜很重要,因此Core Image可以准确地找到人脸。
  •      是否仅应用红眼矫正。 (将kCIImageAutoAdjustEnhance设置为false。)
  •      是否应用除红眼校正之外的所有滤镜。 (将kCIImageAutoAdjustRedEye设置为false。)
autoAdjustmentFiltersWithOptions:方法返回一个选项过滤器数组,然后你将链接到一起并应用于分析的图像,如下图所示。 代码首先创建一个option字典。 然后获取图像的方向并将其设置为关键字CIDetectorImageOrientation的值。
NSDictionary *options = @{ CIDetectorImageOrientation :
                 [[image properties] valueForKey:kCGImagePropertyOrientation] };
NSArray *adjustments = [myImage autoAdjustmentFiltersWithOptions:options];
for (CIFilter *filter in adjustments) {
     [filter setValue:myImage forKey:kCIInputImageKey];
     myImage = filter.outputImage;
}
回想一下,输入参数值已由Core Image设置以产生最佳结果。
你不必立即应用自动调整filter。 你可以保存filter名称和参数值供以后使用。 保存它们可让你的应用程序稍后执行增强功能,而无需再次分析图像的成本。
继承CIFilter:自定义的效果的方法
你可以通过使用一个image filter的输出作为另一个的输入来创建自定义效果,链接许多filter到一起。你用这种方式创建效果——你要多次使用,继承自CIFilter来包含这个效果作为一个filter。
这里重要介绍Core Image如何继承CIFilter来创建CIColorInvert filter。然后介绍了多个filter链接到一起来实现有趣效果的方法。通过继承操作,你应该创建你自己的组合的Core Image内建filter。
继承CIFilter来创建CIColorInvert filter
当你继承CIFilter,你可以通过修改预设值代码或者链接filter来修改已知的filter。Core Image用这种技术实现某些内置的filter。
为了子类话一个filter,你需要以下几步:
  • 为filter的输入参数声明属性,属性的前缀必须为“input”,比如“inputImage”
  • 重写 setDefaults方法,如果需要的话。(本示例中没必要,因为输入参数是设置值)
  • 重写outputImage方法
Core Image提供的CIColorInvert filter是一个CIColorMatrix filter的变化特例。和字面意思一样,CIColorInvert 提供反转输入图片颜色的向量vectors给CIColorMatrix。如下:
 CIColorInvert filter的接口:
@interface CIColorInvert: CIFilter {
CIImage *inputImage;
}
@property (retain, nonatomic) CIImage *inputImage;
@end
CIColorInvert filter的输出图片方法:
@implementation CIColorInvert
@synthesize inputImage;
– (CIImage *) outputImage
{
CIFilter *filter = [CIFilter filterWithName:@”CIColorMatrix”
withInputParameters: @{
kCIInputImageKey: inputImage,
@”inputRVector”: [CIVector vectorWithX:-1 Y:0 Z:0],
@”inputGVector”: [CIVector vectorWithX:0 Y:-1 Z:0],
@”inputBVector”: [CIVector vectorWithX:0 Y:0 Z:-1],
@”inputBiasVector”: [CIVector vectorWithX:1 Y:1 Z:1],
}];
return filter.outputImage;
}
Chroma Key Filter Recipe
从一个源图移除一个或者一个范围的颜色然后用一个背景图组合一个源图。
The Chroma Key filter processing chain:

 

以下是如何创建 Chroma Key Filter
  • 创建一个映射你要移除的色值的数据的 cube map ,这样他们就是透明的了(aplha 0.0)
  • 使用CIColorCube filter和上面的cube map来从源图中移除chroma-key color
  • 使用 CISourceOverCompositing filter来组合处理的图片到一个背景图上。
下面详细介绍:

创建一个 Cube Map

color cube 是一个 3D的色彩查找表。Core Image filter CIColorCube 用 色值作为输入,把输入应用給查找table。COColorCube默认table是一个标识的矩阵——意味着不会给输入数据带来变化。但是,本文提到的这个方法要求你从图像中移除所有的绿色。当然你也可以移除其他颜色。
通过设置绿色的aphpa为0.0,在图像中移除所有绿色。“绿色”包含了一个范围的颜色。最直接的处理方式是在图像中,把色值从RGBA值转到HSV值。在HSV中,hue代表了一个圆柱体的中心轴的角度。在这个表示法中,你可以把颜色想象成一个饼状片,然后简单地删除代表色键颜色的切片。
要去除绿色,你需要定义包含绿色hues的中央访问的最小和最大角度。然后,对于任何绿色的东西,你将它的alpha值设置为0.0。纯绿色的值对应于120度。最小和最大角度需要围绕这个值。
cube map数据必须是预乘的alpha值,所以创建cube map映射的最后一步是将RGB值乘以你刚刚计算的alpha值,它要么是0,要么是绿色,要么是0。下面展示了如何为这个filter方法创建所需的color cube map。
// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float rgb[3], hsv[3], *c = cubeData;

 

// Populate cube with a simple gradient going from 0 to 1
for (int z = 0; z < size; z++){
    rgb[2] = ((double)z)/(size-1); // Blue value
    for (int y = 0; y < size; y++){
        rgb[1] = ((double)y)/(size-1); // Green value
        for (int x = 0; x < size; x ++){
            rgb[0] = ((double)x)/(size-1); // Red value
            // Convert RGB to HSV
            // You can find publicly available rgbToHSV functions on the Internet
            rgbToHSV(rgb, hsv);
            // Use the hue value to determine which to make transparent
            // The minimum and maximum hue angle depends on
            // the color you want to remove
            float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;
            // Calculate premultiplied alpha values for the cube
            c[0] = rgb[0] * alpha;
            c[1] = rgb[1] * alpha;
            c[2] = rgb[2] * alpha;
            c[3] = alpha;
            c += 4; // advance our pointer into memory for the next color value
        }
    }
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
                       length:cubeDataSize
                       freeWhenDone:YES];
CIColorCube *colorCube = [CIFilter filterWithName:@”CIColorCube”];
[colorCube setValue:@(size) forKey:@”inputCubeDimension”];
// Set data for cube
[colorCube setValue:data forKey:@”inputCubeData”];

从源图移除绿色

现在你有了一个 color map data,内部有你想要移除绿色的前景图的数据。而且装入了CIColorCubefilter,可以获得输出了。
[colorCube setValue:myInputImage forKey:kCIInputImageKey];
CIImage *result = [colorCube valueForKey:kCIOutputImageKey];

把处理的源图混合到一个背景图上

给CISourceOverCompositing filter设置输入参数如下:
  • 从CIColorCube filter的输出的image作为输入给CISourceOverCompositing filter
  • 给 inputBackgroundImage 设置你需要的图片。
齐活。
Face Filter 添加白色虚化效果的方法
在图像中检测到的人脸的边缘增加一个image的亮度。效果如下:
创建白色虚化filter需要:
  • 找到人脸
  • 使用CIRadialGradient filter 在人脸中心创建一个base shade map
  • 用原始图混合base shade map

找到人脸

使用CIDetector 类定位人脸。featuresInImage:options: 方法返回操作的特征数组。拿到面部数据后,计算面部的中心位置,代码如下:
CIDetector *detector = [CIDector detectorOfType:CIDetectorTypeFace
                                        context:nil
                                        options:nil];
NSArray *faceArray = [detector featuresInImage:image options:nil];
CIFeature *face = faceArray[0];
CGFloat xCenter = face.bounds.origin.x + face.bounds.size.width/2.0;
CGFloat yCenter = face.bounds.origin.y + face.bounds.size.height/2.0;
CIVector *center = [CIVector vectorWithX:xCenter Y:yCenter];

创建一个 Shade Map

使用 CIRadialGradient filter在面部中心创建shade map。shade map中心应该是透明的,这样人脸就能保证可见。map的边缘应该是不透明的白。脸和map边缘的范围区域应该有渐变透明变化。
这样给 CIRadialGradient 设置参数:
  • Set inputRadius0 to a value larger than the longest dimension of the the image.
  • Set inputRadius1 to a value larger than the face, such as face.bounds.size.height + 50.
  • Set inputColor0 to opaque white.
  • Set inputColor1 to transparent white.
  • Set the inputCenter to the center of the face bounds that you computed

用人脸混合渐变

CISourceOverCompositing 设置输入参数:inputImage是源图,inputBackgroundImage是上一步的 shade map。
齐活。
移轴filter 方法 / Tilt-Shift Filter Recipe
选择性地聚焦一个图像来模拟一个微型场景。
移轴filter 处理过程:
创建一个 tilt-shift filter:
  • 创建一个焦点的版本的图片
  • 创建两个线性渐变
  • 通过组合上面的渐变创建一个蒙版
  • 组合焦点图、蒙版和源图

创建一个焦点的版本的图片

设置 CIGaussianBlur filter,设置输入参数 inputImage,设置inputRadius 为 10.0(默认)

创建两个线性渐变

从上到下,从单一颜色(如绿色或灰色)创建一个线性渐变。CILinearGradient 设置输入参数:Set inputPoint0 to (0, 0.75 * h) Set inputColor0 to (0,1,0,1) Set inputPoint1 to (0, 0.5*h) Set inputColor1 to (0,1,0,0),然后创建一个从底部到顶部的绿色线性渐变。将CILinearGradient的输入参数设置:Set inputPoint0 to (0, 0.25 * h) Set inputColor0 to (0,1,0,1),Set inputPoint1 to (0, 0.5*h),Set inputColor1 to (0,1,0,0)

通过组合上面的渐变创建一个蒙板

设置 CIAdditionCompositing filter 来创建一个蒙板:Set inputImage to the first linear gradient you created. Set inputBackgroundImage to the second linear gradient you created.

组合焦点图、蒙板和源图

使用 CIBlendWithMask filter,设置参数:Set inputImage to the blurred version of the image. Set inputBackgroundImage to the original, unprocessed image. Set inputMaskImage to the mask, that is, the combined gradients.
这个蒙板只会影响图像的外部部分。蒙板的透明部分将通过原始的、未经处理的图像显示出来。蒙板的不透明部分让模糊的图像显示出来。
马赛克面部filter 方法
顾名思义。效果如下:
创建马赛克filter :
  • 创建像素化版本的图片
    • 设置CIPixellate filter,inputImage是有人脸的源图
    • 设置inputScale为max(width, height)/60 或其他值,width, height是image的width和height
  • 使用图片面部识别的face构建蒙板
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
                                          context:nil
                                          options:nil];
NSArray *faceArray = [detector featuresInImage:image options:nil];

 

// Create a green circle to cover the rects that are returned.

 

CIImage *maskImage = nil;

 

for (CIFeature *f in faceArray) {
    CGFloat centerX = f.bounds.origin.x + f.bounds.size.width / 2.0;
    CGFloat centerY = f.bounds.origin.y + f.bounds.size.height / 2.0;
    CGFloat radius = MIN(f.bounds.size.width, f.bounds.size.height) / 1.5);
    CIFilter *radialGradient = [CIFilter filterWithName:@”CIRadialGradient” withInputParameters:@{
        @”inputRadius0″: @(radius),
        @”inputRadius1″: @(radius + 1.0f),
        @”inputColor0″: [CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0],
        @”inputColor1″: [CIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.0],
        kCIInputCenterKey: [CIVector vectorWithX:centerX Y:centerY],
        }];
    CIImage *circleImage = [radialGradient valueForKey:kCIOutputImageKey];
    if (nil == maskImage)
        maskImage = circleImage;
    else
        maskImage = [[CIFilter filterWithName:@”CISourceOverCompositing” withInputParameters:@{
            kCIInputImageKey: circleImage,
            kCIInputBackgroundImageKey: maskImage,
            }] valueForKey:kCIOutputImageKey];
}
  • 用蒙板混合像素化图片和源图
    • 使用CIBlendWithMask filter,设置inputImage为像素化图,设置inputBackgroundImage为源图,设置 inputMaskImage 为组合的模糊蒙板。
像素变换filter方法
通过像素化每个图像从一个图像到另一个图像的转换。
创建一个像素变换filter需要:
  • 使用CIDissolveTransition 在源图和目标间过渡
  • 像素化过渡filter的结果

使用CIDissolveTransition 在源图和目标间过渡

设置 CIDissolveTransition filter
  • Set inputImage to the image from which you want to transition.
  • Set inputTagetImage to the image to which you want to transition.
  • Set inputTime to a value similar to min(max(2*(time – 0.25), 0), 1), which is a ramp function that’s clamped between two values.

像素化过渡filter的结果

设置 CIPixellate filter ,通过设置输入参数来改变像素的大小:
  • Set inputImage to the output image from the CIDissolveTransition filter.
  • Set inputScale to change over time by supplying values from a triangle function: 90*(1 – 2*abs(time – 0.5))
  • Use the default value for inputCenter.
旧电影filter方法
通过降低视频图片质量来让他看起来像个老电影。
处理链路如下:
旧电影filter需要:
  • 给源视频图片应用 CISepiaTone filter
  • 创建所及变化white specks
  • 创建随机变化black specks
  • 组合以上specks和scratches给 sepia-toned 的图片

给源视频图片应用 CISepiaTone filter

CISepiaTone 设置如下:
  • Set inputImage to the video image to apply the effect to.
  • Set inputIntensity to 1.0.

创建所及变化white specks

CIRandomGenerator能生成有色彩的燥点。没有输入参数。所以为了处理燥点,使用 CIColorMatrix filter:
  • Set inputImage to the output produced by the random generator.
  • Set inputRVector, inputGVector, and inputBVector to (0,1,0,0).
  • Set inputBiasVector to (0,0,0,0).
使用CISourceOverCompositing filter来汇合上面的specks和视频图像,设置以下参数:
  • Set inputImage to the white-specks image produced by the CIColorMatrix filter.
  • Set inputBackgroundImage to the image produced by the CISepiaTone filter.

创建随机变化black specks

再次使用 CIRandomGenerator filter 来生成有颜色的噪点。然后出CIAffineTransform filter 处理燥点:
  • Set inputImage to the noise generated by the CIRandomGenerator filter.
  • Set inputTransform to scale x by 1.5 and y by 25. This makes the pixels thick and long, but they will still be colored.
使用CIAffineTransform的 imageByApplyingTransform: 方法来修改噪点变换。
为了让像素变暗,设置 CIColorMatrix filter参数如下:
  • Set inputImage to the transformed video image.
  • Set inputRVector to (4,0,0,0).
  • Set inputGVector, inputBVector, and inputAVector to (0,0,0,0).
  • Set inputBiasVector to (0,1,1,1).
这时结果会变成 cyan-colored 青色的划痕状。为了使划痕变暗,将CIMinimumComponent过滤器应用到cyan-colored 的划痕上。该filter使用r g b值的最小值来生成灰度图像。

组合以上specks和scratches给 sepia-toned 的视频图片

使用 CIMultiplyCompositing filter :
  • SetinputBackgroundImage to the processed video image (sepia tone, white specks).
  • SetinputImage to the dark scratches, that is, the output from the CIMinimumComponent filter.
如何得到最佳性能
核心图像提供许多选项来创建 图片,上下文和绘制内容。基于以下条目来选择他们:
  • 你app需要执行任务的频率 how offen?
  • 你app 是不是对于定格图片和视屏图像都有效
  • 是否需要支持实时处理和分析
  • 色彩保真度对用户有多重要
基于以上,接下来提供性能建议。
关于性能最佳实践
  • 每次绘制时,不需要每次都创建CIContext 对象。Contexts存储了过多状态信息,最好是重用他们。
  • 评估你的app是否需要色彩管理。在需要使用时才使用。参考  Does Your App Need Color Management?.
  • 当用CPU context绘制CIImage对象时,应避免 Core Animation 的动画。,如果你需要同时使用他们,你需要在CPU设置。
  • 确保图片不会超过 CPUGPU的限制。CIContext对象的图像大小限制取决于Core Image是否使用CPUGPU。使用inputImageMaximumSizeoutputImageMaximumSize的方法来检查限制。
  • 尽量使用更小的图。性能和输出像素的数量相关。你可以将Core Image渲染成更小的视图、纹理或framebuffer。允许Core Animation升级显示尺寸。使用Core GraphicsImage I/O函数来进行裁剪或向下采样,例如函数CGImageCreateWithImageInRectCGImageSourceCreateThumbnailAtIndex.
  • UIImageView类和静态图配合使用最佳。如果你app需要有最佳性能,使用更底层的api
  • 避免不必要的CPUGPU之间的纹理传输。
  • 在应用内容的scale factor之前,渲染与源图像相同大小的矩形。
  • 考虑使用更简单的filter,它可以产生类似于algorithmic filters的结果。比如,CIColorCube 的输出和CISepiaTone类似,但是更有效率。
  • iOS 6.0和更高版本中,利用对YUV图像的支持。Camera pixel buffers是原生YUV,但大多数图像处理算法都基于RBGA数据。这两者之间的转换是有代价的。核心图像支持从CVPixelBuffer对象读取YUB,并应用适当的颜色转换。如下:
options = @{ (id)kCVPixelBufferPixelFormatTypeKey :
@(kCVPixelFormatType_420YpCbCr88iPlanarFullRange) };
你确实需要色彩管理?
默认,Core Image 对所有 filters应用 light-linear color space。这提供了最精确和一致的结果。sRGB的转换增加了filter的复杂性,并且需要Core Image来应用以下这些等式:
rgb = mix(rgb.0.0774, pow(rgb*0.9479 + 0.05213, 2.4), step(0.04045, rgb))
rgb = mix(rgb12.92, pow(rgb*0.4167) * 1.055 – 0.055, step(0.00313, rgb))
以下情况考虑禁用色彩管理
  • 你app需要绝对的高性能
  • 在夸张的操作之后,用户不会注意到质量差异
要禁用颜色管理,请将kCIImageColorSpace键设置为null。如果你使用的是EAGL的上下文,当你创建EAGL的上下文时,也会将context colorspace设置为null,参考 Building Your Own Workflow with a Core Image Context
使用反馈来处理图片
CIImageAccumulator 类非常适合基于反馈的处理。正如其名,它会不停的累积啊图片数据。下面介绍如何使用 CIImageAccumulator 对象来实现一个简单的绘图app——MicroPaint,可以让用户在一个画布上绘制图像。
图像开始于一个空画布。MicroPaint 使用一个图像 accumulator 来手机用户的绘画。当用户点击 Clear,App重置 图像 accumulator 到一个白色画布。颜色可以让用户改变油漆颜色。用户可以使用滑块改变画笔大小。
必要的创建app 的步骤:
  1. Set Up the Interface for the MicroPaint App
  2. Initialize Filters and Default Values for Painting
  3. Track and Accumulate Painting Operations
本章只描述了创建image accumulator和支持绘图所必需的代码。这里没有讨论绘制视图和处理视图大小变化的方法。为此,请参阅CIMicroPaint,这是一个完整的示例代码项目,你可以更详细地下载和检查。CIMicroPaint有几个有趣的细节。它展示了如何绘制OpenGL视图,并维护以前版本的OS x的向后兼容性。
设置 App 界面
  • 一个图像累加器
  • 为用户提供“画笔”。 画笔是一个Core Image filter(CIRadialGradient),以模拟气刷的方式应用颜色。
  • 一种组合 filter(CISourceOverCompositing),允许将新油漆合成到以前应用的油漆上。
  • 用于跟踪当前涂料颜色和画笔大小的变量。
建立一个filter字典,将MircoPaintView声明为SampleCIView的子类。这里没有讨论SampleCIView类;它是NSOpenGLView类的子类。详情请参阅CIMicroPaint 样例应用程序。
@interface MicroPaintView : SampleCIView {
CIImageAccumulator *imageAccumulator;
CIFilter *brushFilter;
CIFilter *compositeFilter;
NSColor *color;
CGFloat brushSize;
}
@end
初始化filters和画图默认值
当你初始化MicroPaint app时,你需要创建画笔和组合滤镜,并设置初始画笔大小和油漆颜色。 下表中的代码被创建并初始化为透明黑色,输入半径为0.当用户拖动光标时,画笔过滤器将显示画笔大小和颜色的当前值。
brushFilter = [CIFilter filterWithName: @”CIRadialGradient” withInputParameters:@{
@”inputColor1″: [CIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.0],
@”inputRadius0″: @0.0,
}];
compositeFilter = [CIFilter filterWithName: @”CISourceOverCompositing”];
brushSize = 25.0;
color = [NSColor colorWithDeviceRed: 0.0 green: 0.0 blue: 0.0 alpha: 1.0];
跟踪和累加绘画操作
鼠标拖动:每当用户点击或拖动光标到画布上时,就会调用方法。它更新画笔和合成滤镜的值,并为累积的图像添加新的绘画操作。 在设置图像之后,您需要触发display的更新。您的drawRect:方法处理绘制图像。当绘制到CIContext对象时,请确保使用了druse:fromRect:fromRect:而不是弃用的方法drawImage:atPoint:fromRect:.
– (void)mouseDragged:(NSEvent *)event
{
CGRect   rect;
NSPoint  loc = [self convertPoint: [event locationInWindow] fromView: nil];
CIColor   *cicolor;
// Make a rectangle that is centered on the drag location and
// whose dimensions are twice of the current brush size
rect = CGRectMake(loc.x-brushSize, loc.y-brushSize,
2.0*brushSize, 2.0*brushSize);
// Set the size of the brush
// Recall this is really a radial gradient filter
[brushFilter setValue: @(brushSize)
forKey: @”inputRadius1″];
cicolor = [[CIColor alloc] initWithColor: color];
[brushFilter setValue: cicolor  forKey: @”inputColor0″];
[brushFilter setValue: [CIVector vectorWithX: loc.x Y:loc.y]
forKey: kCIInputCenterKey];
// Composite the output from the brush filter with the image
// accummulated by the image accumulator
[compositeFilter setValue: [brushFilter valueForKey: kCIOutputImageKey]
forKey: kCIInputImageKey];
[compositeFilter setValue: [imageAccumulator image]
forKey: kCIInputBackgroundImageKey];
// Set the image accumluator to the composited image
[imageAccumulator setImage: [compositeFilter valueForKey: kCIOutputImageKey]
dirtyRect: rect];
// After setting the image, you need to trigger an update of the display
[self setImage: [imageAccumulator image] dirtyRect: rect];
}

留下评论

Your email address will not be published. Required fields are marked *