The article comes from "functional programing in Swift", this series is just a summary of some of the contents of the post-viewing
Wrapping Core Image
In the previous article, we introduced higher-order functions and demonstrated how functions are passed as parameters to other functions. In this chapter, we will show how to use higher-order functions to function-wrap an existing object-oriented API.
The Core Image is a very powerful framework for graphics processing, but sometimes its API is a bit cumbersome to use. The Coreimage API is loosely typed--image filters is configured using Key-value coding. This makes it easy to make errors in the type and the name of the parameter, resulting in a run-time error. Our new API will be secure and modular, using types to ensure there are no such run-time errors.
The Filter Type
Cifilter is used to create an image filter (filter), when you initialize a Cifilter object, you always provide an input image through the Kciinputimagekey, and then pass the Kcioutputimagekey to get the filtered result. You can then use the result as input to the next filter.
We will try to encapsulate the specifics of these key-value pairs and provide a secure, strongly typed API to our users.
Typealias Filter = ciimage-Ciimage
This is the basic type we're going to build.
Building Filters
Now that we have defined the basic types of filters, we can begin to define related functions for specific filters, and the parameters of these handy functions require a specific filter and return a filter.
Func myfilter (/* parameters */) Filter
Note The return filter type, which is also a function. Since then, this will help us assemble multiple filters to achieve the desired image effect.
To make us lives a bit easier, we will extend the Cifilter class by convenience initializer and a computed property to retrieve the output image
Typealias Parameters = dictionary<string, anyobject>extension cifilter { convenience init (name:string, Parameters:parameters) { self.init (name:name) setdefaults () for (key, value:anyobject) in Parameters { C4/>setvalue (value, Forkey:key) } } var outputimage:ciimage { return Self.valueforkey ( Kcioutputimagekey) as Ciimage } }
Our convenience constructor first invokes our specified constructor. This computed property outputimage provides an easy way to get the output image from the filter object. With this computed attribute, users who use our API no longer need to care about how to get such an operation.
Blur (Blur)
Blur filter requires only one blur radius as its parameter
Func blur (radius:double), Filter {return {image in let parameters:parameters = [ Kciinputradiuskey:radiu S, kciinputimagekey:image ] let filter = Cifilter (name: "Cigaussianblur", Parameters:parameters) Return Filter.outputimage }}
Blur functions returns a function that has an image of type ciimage and returns a new image. Because of this, the return value of the fuzzy function conforms to the filter type we defined earlier
(Typealias filter = Ciimage, ciimage). In this example, we are simply wrapping the filter in the original core image. We can use the same pattern over and over again to create our own filtering capabilities.
Color Overlay
We define a filter that overrides the color we selected on the image. There is no such filter by default in core image, but we can, of course, be made up of existing filters.
These two building blocks are we want to use the Color generator filter (ciconstantcolorgenerator) and Source-over compositing filter (cisourceovercompositing).
Func Colorgenerator (color:nscolor), Filter { Return {_ in let parameters:parameters = [kciinputcolorkey:c Olor] Let filter = Cifilter (name: "Ciconstantcolorgenerator", Parameters:parameters) return Filter.outputimage }}
This is similar to the blur filter we defined earlier, but with a different point: this constant color generator filter is not associated with imput image, so we don't need the image parameter.
Func compositesourceover (overlay:ciimage), Filter { return {image in let parameters:parameters = [ KCI Inputbackgroundimagekey:image, kciinputimagekey:overlay ] let filter = Cifilter (name: " Cisourceovercompositing ", parameters:parameters) let croprect = image.extent () return Filter.outputImage.imageByCroppingToRect (Croprect)}}
Here we clip the size of the output image to the size of the input image. This is not necessary, depending on the behavior of our filter. However, the work in this example is very good.
Finally, we combine the two filter into a color overlay filter:
Func Coloroverlay (color:nscolor), Filter { return {image in let overlay = Colorgenerator (color) (image)
return compositesourceover (overlay) (image)
}}
Composing Filters
Now we use our defined filter to apply to the actual picture:First we blur the image, and then we put a red overlay on top.
Let URL = Nsurl (string: "Http://tinyurl.com/m74sldb") Let image = Ciimage (contentsofurl:url)//Now we can apply both Filte RS to these by chaining them together:let Blurradius = 5.0let Overlaycolor = Nscolor.redcolor (). colorwithalphacomponent (0. 2) Let Blurredimage = Blur (Blurradius) (image) Let Overlaidimage = Coloroverlay (Overlaycolor) (blurredimage)
Function composition
Of course, the above filter combination we can use a statement:
Let result = Coloroverlay (Overlaycolor) (Blur (Blurradius) (image))
However, the code quickly becomes unreadable. A good way to do this is to customize the operation of a filter combination:
Func composefilters (Filter1:filter, Filter2:filter), Filter { return {img in Filter2 (Filter1 (IMG))}}
Let MyFilter1 = Composefilters (Blur (Blurradius), Coloroverlay (Overlaycolor)) Let RESULT1 = MyFilter1 (image)
We can go further and make this more readable by using custom operators to combine
Infix operator >>> {associativity left}func >>> (Filter1:filter, Filter2:filter), Filter { return {img in Filter2 (Filter1 (IMG)}}}
Let MyFilter2 = Blur (Blurradius) >>> coloroverlay (overlaycolor) Let result2 = MyFilter2 (image)
When we define >>> for the left to combine
Theoretical background:currying Curry function
The relevant knowledge about the function of the curry, not much introduction, the following links:
Swift's currying
Discussion
In this chapter, we will find that the API we have designed has several advantages:
1. Safety----It is almost impossible to create runtime errors arising from undefined keys or failed casts
2. Modularity---it is easy to compose filters using the >>> operator. Doing so allows-tease apart complex filters into smaller, simpler, reusable components. Additionally, composed filters has the exact same type as their building blocks, so can use them interchangeably.
3. Clarity---- Even if you had never used Core Image, you should being able to assemble simple filters using the F Unctions we have defined. To access the results, you don ' t need to know about special dictionary keys, such as Kcioutputimagekey, or worry about INI Tializing certain keys, such as Kciinputimagekey or Kciinputradiuskey. From the types alone, you can almost figure out how to use the API, even without further documentation
Swift's functional Programming (III.)