I wrote the following Metal Core Image Kernel to produce constant red color.
extern "C" float4 redKernel(coreimage::sampler inputImage, coreimage::destination dest)
{
return float4(1.0, 0.0, 0.0, 1.0);
}
And then I have this in Swift code:
class CIMetalRedColorKernel: CIFilter {
var inputImage:CIImage?
static var kernel:CIKernel = { () -> CIKernel in
let bundle = Bundle.main
let url = bundle.url(forResource: "Kernels", withExtension: "ci.metallib")!
let data = try! Data(contentsOf: url)
return try! CIKernel(functionName: "redKernel", fromMetalLibraryData: data)
}()
override var outputImage: CIImage? {
guard let inputImage = inputImage else {
return nil
}
let dod = inputImage.extent
return CIMetalTestRenderer.kernel.apply(extent: dod, roiCallback: { index, rect in
return rect
}, arguments: [inputImage])
}
}
As you can see, the dod is given to be the extent of the input image. But when I run the filter, I get a whole red image beyond the extent of the input image (DOD), why? I have multiple filters chained together and the overall size is 1920x1080. Isn't the red filter supposed to run only for DOD rectangle passed in it and produce clear pixels for anything outside the DOD?
For additional followup questions:
..on what extent rect is CIKernel processing done (it can't be infinite)
It can be infinite and the value returned by dest.coord()
in your kernels can be values in an infinite space. In practice though, when your image is rendered only a finite number of pixels will be rendered.
How do I know the limits of destination coordinates in the kernel? I only get normalised texture coordinates in the kernel for the current pixel in processing. But just like Metal compute kernel if I need absolute coordinates I am currently computing, how do I get it? Simple example being if I need to put a 10 pixel red border around the border of input image, I don't see a straight forward way for the same.
This code snippet and kernel will add a red border to an image.
static dispatch_once_t onceToken;
static CIColorKernel *k;
dispatch_once(&onceToken, ^{
k = [CIColorKernel kernelWithFunctionName:@"red_border" fromMetalLibraryData:metalLibData() error:nil];
});
CGRect iExtent = image.extent;
return [k applyWithExtent:image
arguments:@[image, [CIVector vectorWithCGRect:iExtent], @(10)]];
float4 red_border (sample_t i, float4 extent, float s, destination dest)
{
float2 dc = dest.coord();
// if the current location is within 's' of 'extent'
if (dc.x < extent.x + s ||
dc.y < extent.y + s ||
dc.x > extent.x + extent.z - s ||
dc.y > extent.y + extent.w - s)
{
// return "red" multiplied by the image's alpha
return float4(1.,0.0,0.,1.) * i.a;
}
return i;
}