In the last few weeks in my spare time I've tried to convert a `CIFilter` with a custom color kernel from a string to a metal written kernel.
The purpose of this filter is to pixellate an image and then apply a custom palette of colors. Pixellation is made by CI Pixellate and then I apply this shader.
float4 eight_bit_buffer(sampler image, constant simd_float3 palette[], float paletteSize, destination dest) {
float4 color = image.sample(image.transform(dest.coord()));
float dist = distance(color.rgb, palette[0]);
float3 returnColor = palette[0];
for (int i = 1; i < floor(paletteSize); ++i) {
float tempDist = distance(color.rgb, palette[i]);
if (tempDist < dist) {
dist = tempDist;
returnColor = palette[i];
}
}
return float4(returnColor, 1);
}
The first argument is the image the second is an array of float3 that makes the palette of color.
Since iOS13 there was written this in the change note:
Metal CIKernel instances support arguments with arbitrarily structured data but I couldn't find any reference in the the documentation or videos.
I've tried to do my myself basically trying to create an Data object and pass it to the shader function. At a first try I simply created a Data object and appended SIM3<Double> to it, but it wasn't working so I tought it could be an issue with memory allignment and I came up with this, but it won't work either.
func toSIMD3() -> SIMD3<Float> {
return [Float(r) / 255.0, Float(g) / 255.0, Float(b) / 255.0]
}
static func SIMD3Palette(_ palette: [RGB]) -> [SIMD3<Float>] {
return palette.map{ $0.toSIMD3() }
}
static func toSIMD3Buffer(from palette: [RGB]) -> Data {
var simd3Palette = SIMD3Palette(palette)
let size = MemoryLayout<SIMD3<Float>>.size
let count = palette.count
let palettePointer = UnsafeMutableRawPointer.allocate(
byteCount: count * MemoryLayout<SIMD3<Float>>.stride,
alignment: MemoryLayout<SIMD3<Float>>.alignment)
let simd3Pointer = simd3Palette.withUnsafeMutableBufferPointer { (buffer) -> UnsafeMutablePointer<SIMD3<Float>> in
let p = palettePointer.initializeMemory(as: SIMD3<Float>.self,
from: buffer.baseAddress!,
count: buffer.count)
return p
}
let data = Data(bytesNoCopy: simd3Pointer, count: count * MemoryLayout<simd3>.stride, deallocator: .free)
return data
}
Have anyone discovered how to pass arbitrarily structured data to a CIKernel?
Best,
Andrea