Вот источник Metal для ядра, которое пытается воспроизвести описанный вами фильтр:
#include <metal_stdlib>
#include <CoreImage/CoreImage.h>
using namespace metal;
extern "C" {
namespace coreimage {
float4 sketch(sampler src, float texelWidth, float texelHeight, float intensity40) {
float size = 1.25f + (intensity40 / 100.0f) * 2.0f;
float minVal = 1.0f;
float maxVal = 0.0f;
for (float x = -size; x < size; ++x) {
for (float y = -size; y < size; ++y) {
float4 color = src.sample(src.coord() + float2(x * texelWidth, y * texelHeight));
float val = (color.r + color.g + color.b) / 3.0f;
if (val > maxVal) {
maxVal = val;
} else if (val < minVal) {
minVal = val;
}
}
}
float range = 5.0f * (maxVal - minVal);
float4 outColor(pow(1.0f - range, size * 1.5f));
outColor = float4((outColor.r + outColor.g + outColor.b) / 3.0f > 0.75f ? float3(1.0f) : outColor.rgb, 1.0f);
return outColor;
}
}
}
Я предполагаю, что вы уже знакомы с основами того, как правильно создавать металлические шейдерыв библиотеку, которая может быть загружена Core Image.
Вы можете создать свое ядро во время выполнения, загрузив библиотеку Metal по умолчанию и запросив функцию "sketch" (имя произвольное, если оно соответствует ядру).source):
NSURL *libraryURL = [NSBundle.mainBundle URLForResource:@"default" withExtension:@"metallib"];
NSData *libraryData = [NSData dataWithContentsOfURL:libraryURL];
NSError *error;
CIKernel *kernel = [CIKernel kernelWithFunctionName:@"sketch" fromMetalLibraryData:libraryData error:&error];
Затем вы можете применить это ядро к изображению, обернув его в свой собственный CIFilter
подкласс, или просто вызвать его напрямую:
CIImage *outputImage = [kernel applyWithExtent:CGRectMake(0, 0, width, height)
roiCallback:^CGRect(int index, CGRect destRect)
{ return destRect; }
arguments:@[inputImage, @(1.0f/width), @(1.0f/height), @(60.0f)]];
У меня естьпытался выбрать разумные значения по умолчанию для каждого из аргументов (первый из которых должен быть экземпляром CIImage
), но, конечно, их можно настроить по вкусу.