Chaining Multiple CIFilters

Chaining CIFilters

Swift의 문법적 특징은 Objective-C를 사용할 때보다 훨씬 더 간결하고 간단한 구조의 코드를 작성할 수 있게 해주는데, 이번에는 Swift의 문법을 활용하여 CIFilter를 연쇄적으로 적용하는 자료 구조를 만들어 보도록 하겠다.

기본적으로 하나의 이미지에 여러 개의 필터를 적용하는 경우에는 각 필터의 outputImage 속성을 다음 순서의 필터의 kCIInputImageKey로 전달하는 방식으로 처리할 수 있다.

이를 아예 연산자로 정의하는 것도 가능하다. 이미지 인스턴스는 필터라는 상태로 둘러싸여 그 내부에서 조작되는 엔티티로 볼 수 있기 때문에 필터 자체를 모나드로 볼 수 있다. 따라서 이미지 -> 필터 -> 이미지 형태의 함수를 연산자로 정의해보면,

infix operator >= { associativity left }
func >= (lhs:CIImage, rhs:CIFilter) -> CIImage{
    rhs.addValue(lhs, forKey:kCIInputImageKey)
    return rhs.outputImage
}

이런 연산자를 정의하면 someCIImage >= someFilter 와 같은 식으로 연산[1. 이 연산은 좌변의 이미지를 우변의 필터에 적용한 이미지를 리턴한다.]하여 오른쪽 필터에 좌변의 이미지를 적용한 이미지를 얻을 수 있으며, 이를 연쇄적으로 적용하는 것도 가능해진다. 필터를 큐나 배열에 넣고 이미지 하나를 받아서 연쇄적으로 필터를 적용하려면,

struct FilterChain {
    var _filters = [CIFilter]()
    mutating func append(newFilter:CIFilter) {
        _filters.append(newFilter)
    }
    mutating func clear() {
        _filters.removeAllItems()
    }
    func apply(img:CIImage) -> CIImage {
        let result:CIImage = _filters.reduce(img, >=)
        return result
    }
}

조절된 각 필터를 추가하고 이미지를 한 번에 저장하는 방법도 가능하다. 이 때 연산자(연산자도 일종의 함수이자 클로저이므로)를 reduce 함수에 넣어 매우 간단한 문법으로 구현할 수 있다.