На iOS вы можете добавить несколько CIF-фильтров к узлу SpriteKit? - PullRequest
1 голос
/ 07 апреля 2019

В iOS, вы можете добавить более одного CIFilter к SKEffectsNode?

CIFilterGenerator похоже на то, что я хочу, но на iOS его нет.

Я знаю, что вы можете использовать несколько фильтров для изображения , передавая выходные данные одного как входные данные следующего, но это бесполезно, если вы хотите воздействовать на узлы, не относящиеся к изображению.

Значит ли это, что мне нужно создать искусственную иерархию SKEffectNode и добавить фильтр к каждому из них, с моим фактическим содержанием в самом низу? Есть ли лучший способ?

Ответы [ 2 ]

2 голосов
/ 08 апреля 2019

Следуя полезному предложению dfd, я остановился на этом простом подклассе. Я отмечаю его ответ правильным, потому что а) он предложил этот подход, и я хочу отдать ему должное, и б) он имеет более общую информацию об использовании CIFilterConstructor для регистрации вашего фильтра.

Полезные ссылки: - Apple Docs - Похожие вопросы - Бесплатная электронная книга Core Image

class MyChainFilter: CIFilter {
    let chainedFilters: [CIFilter]
    @objc dynamic var inputImage: CIImage?

    init(filters: [CIFilter]) {
        self.chainedFilters = filters
        super.init()
    }

    // run filters in order on the specified source image
    override var outputImage: CIImage? {
        get {
            let imageKey = "inputImage"
            var workingImage = self.inputImage
            for filter in chainedFilters {
                assert(filter.inputKeys.contains(imageKey))
                filter.setValue(workingImage, forKey: imageKey)
                guard let result = filter.outputImage else {
                    assertionFailure("filter failed: \(filter.name)")
                    return nil
                }
                workingImage = result
            }
            return workingImage
        }
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
2 голосов
/ 08 апреля 2019

В тех случаях, когда трудно или невозможно «связать» вместе несколько вызовов CIFilter для достижения желаемого эффекта - возможно, из-за класса, имеющего одно свойство, один из способов преодоления этого заключается в следующем:

  • Подкласс CIFilter, переопределяя все, что вам нужно.Это может включать attributes, setValue(forKey:) и, самое главное, outputImage.
  • подкласс CIFilterConstructor и создание метода registerFilter().

Например,Допустим, вы хотите объединить размытие по Гауссу, а затем добавить монохромный красный тон на изображение.По сути, вы можете сделать это:

class BlurThenColor:CIFilter {

    let blurFilter = CIFilter(name: "CIGaussianBlur")

    override public var attributes: [String : Any] {
        return [
            kCIAttributeFilterDisplayName: "Blur then Color",

            "inputImage": [kCIAttributeIdentity: 0,
                           kCIAttributeClass: "CIImage",
                           kCIAttributeDisplayName: "Image",
                           kCIAttributeType: kCIAttributeTypeImage]
        ]
    }
    override init() {
        super.init()
    }
    @available(*, unavailable) required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override public func setValue(_ value: Any?, forKey key: String) {
        switch key {
        case "inputImage":
            blurFilter?.setValue(inputImage, forKey: "inputImage")
        default:
            break
        }
    }
    override public var  outputImage: CIImage {
        return (blurFilter?.outputImage)! .applyingFilter("CIColorMonochrome", parameters: ["inputColor": CIColor(red: 1.0, green: 0.0, blue: 0.0)])
    }
}

Если вы хотите предоставить больше атрибутов, вы можете просто добавить их в переопределения attributes и setValue(forKey:) вдоль добавления переменных wist и setDefaults.Здесь я просто использую значения по умолчанию.

Теперь, когда вы объединили свой эффект в один пользовательский фильтр, вы можете зарегистрировать его и использовать его:

let CustomFilterCategory = "CustomFilter"

public class CustomFilterConstructor: NSObject, CIFilterConstructor {
    static public func registerFilter() {
        CIFilter.registerName(
            "BlurThenColor",
            constructor: CustomFilterConstructor(),
            classAttributes: [
                kCIAttributeFilterCategories: [CustomFilterCategory]
            ])
    }
    public func filter(withName name: String) -> CIFilter? {
        switch name {
        case "BlurThenColor":
            return BlurThenColor()
        default:
            return nil
        }
    }
}

Чтобы использовать это,просто обязательно зарегистрируйте фильтр (я стараюсь поставить мой в AppDelegate, если это возможно):

CustomFilterConstructor.registerFilter()

Оттуда вы можете использовать BlurThenColor, как и любой другой CIFilter.Создайте его, используйте setValue и позвоните outputImage.

Обратите внимание, этот код будет аварийно завершать работу из-за принудительного развертывания inputImage и / или опечаток.Я уверен, что вы можете сделать это более безопасно - но будьте уверены, что я проверил это, и это работает.(Я создал этот пользовательский фильтр и заменил его в приложении, в котором не выполняется принудительное развертывание.)

...