I am trying to create a Cycernel Offset for iOS 8, which moves the pixels horizontally from the channel R of the map and vertically from the G-channel. Map pixel coordinates should be selected relative to the size of the original image mapPixel = ((dest.x / source.width) * map.width, (dest.y / source.height) * map.height)
The size of the input image I'm testing with is 2048 x 2048 and the map is red-green perlin noise 2560 x 2560
In a quartz composer, the looper works almost as expected, except that the card does not apply to the entire image.
kernel vec4 coreImageKernel(sampler image, sampler displaceMap, float scaleX, float scaleY) { vec2 destination = destCoord(); vec2 imageSize = samplerSize(image); float xPercent = destination.x / imageSize.x; float yPercent = destination.y / imageSize.y; vec2 mapSize = samplerSize(displaceMap); vec2 mapCoord = vec2(mapSize.x * xPercent, mapSize.y * yPercent); vec4 mapPixel = sample(displaceMap, mapCoord); float ratioShiftX = ((mapPixel.x) * 2.0) - 1.0; float ratioShiftY = ((mapPixel.y) * 2.0) - 1.0; vec2 pixelShift = vec2(ratioShiftX * scaleX, ratioShiftY * scaleY); return sample(image, destination - pixelShift); }
Here's what the filter function looks like:
function __image main(__image image, __image displaceMap, __number scaleX, __number scaleY) { return coreImageKernel.apply(image.definition, null, image, displaceMap, scaleX, scaleY); }
But when I load the cycleel into CIFilter, the result is far from what I see in Quartz Composer. Here my application function looks like in CIFilter
override var outputImage:CIImage? { if let inputImage = inputImage { if let inputMap = inputMap { let args = [inputImage as AnyObject, inputMap as AnyObject, inputScaleX, inputScaleY] return CIDisplacementMapFilter.kernel?.applyWithExtent(inputImage.extent, roiCallback: { (index, rect) in if index == 0 { return rect } return CGRectInfinite }, arguments: args) } } return nil }
I assume that the ROI is wrong and the sampler is tiled, but I cannot understand.