Skip to content

Instantly share code, notes, and snippets.

@Farini
Last active March 10, 2024 07:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Farini/baa585aab8dfdff5fdae1ff851547216 to your computer and use it in GitHub Desktop.
Save Farini/baa585aab8dfdff5fdae1ff851547216 to your computer and use it in GitHub Desktop.
CIFilter with Metal shader
/**
A CIFilter that uses a Metal function that converts a black pixel (or almost black) to a transparent pixel.
*/
class BLKTransparent: CIFilter {
private var kernel: CIColorKernel
var inputImage: CIImage?
var threshold: Float?
override init() {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
guard let data = try? Data(contentsOf: url) else { fatalError() }
guard let kern = try? CIColorKernel(functionName: "makeBlackTransparent", fromMetalLibraryData: data) else { fatalError() }
self.kernel = kern
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func outputImage() -> CIImage? {
guard let inputImage = inputImage else {return nil}
return kernel.apply(extent: inputImage.extent, arguments: [inputImage, threshold ?? 0.15])
}
}
#include <metal_stdlib>
using namespace metal;
#include <CoreImage/CoreImage.h>
extern "C" { namespace coreimage {
float4 makeBlackTransparent(sample_t sample, float threshold) {
float4 filtered = (sample.r < threshold && sample.g < threshold && sample.b < threshold) == true ? float4(0):float4(sample.r, sample.g, sample.b, sample.a);
return filtered;
}
}}
class ViewModel:ObservableObject {
@Published var openingImage:NSImage
init(image:NSImage?) {
// Init with an image or one will be created
if let image = image {
self.openingImage = image
} else {
self.openingImage = NSImage(named:"Example")! // Make sure there is "Example.png" in your assets
}
}
func metalBlackToTransparent() {
let inputImage:NSImage = openingImage
// Convert NSImage to CIImage
guard let inputData = inputImage.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: inputData),
let inputCIImage = CIImage(bitmapImageRep: bitmap) else {
print("Missing something. Check references.")
fatalError()
}
// Create the filter
let context = CIContext()
let shade = BLKTransparent()
shade.inputImage = inputCIImage
shade.threshold = 0.15
// get a CIImage from our filter or exit if that fails
guard let outputImage = shade.outputImage() else { return }
// attempt to get a CGImage from the CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a NSImage
let nsImage = NSImage(cgImage: cgimg, size:openingImage.size)
self.openingImage = nsImage
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment