Skip to content

Instantly share code, notes, and snippets.

@erikaderstedt
Last active March 20, 2018 15:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erikaderstedt/8257477c84f855bd67cf6dc45f129b77 to your computer and use it in GitHub Desktop.
Save erikaderstedt/8257477c84f855bd67cf6dc45f129b77 to your computer and use it in GitHub Desktop.
//
// Genie.swift
// iOrdning7
//
// Created by Erik Aderstedt on 2016-07-11.
// Copyright © 2016 Aderstedt Software AB. All rights reserved.
//
import Cocoa
import Quartz
let genieKernel: CIKernel = {
guard
let url = Bundle.main.urlForResource("ASGenieKernel", withExtension: "txt"),
let str = try? String(contentsOf: url),
let kernel = CIKernel(string: str) else { fatalError("Resource not accessible") }
return kernel
}()
class GenieFilter: CIFilter {
var inputTime: CGFloat = 2
var inputDelta: CGFloat = 10
var inputTarget: CGFloat = 200
var inputScalingFactor: CGFloat = 1
var inputImage: CIImage? = nil
override func setDefaults() {
super.setDefaults()
inputTime = 2
inputDelta = 10
inputTarget = 200
inputScalingFactor = 1
}
override var attributes: [String : AnyObject] {
return ["inputTime": [kCIAttributeMin: 0.0,
kCIAttributeMax: 2.0,
kCIAttributeSliderMin: 0.0,
kCIAttributeSliderMax: 2.0,
kCIAttributeDefault: 0.0,
kCIAttributeIdentity: 0.0,
kCIAttributeClass: NSNumber.self,
kCIAttributeType: kCIAttributeTypeScalar],
"inputTarget": [kCIAttributeSliderMin: 0.0,
kCIAttributeSliderMax: 500.0,
kCIAttributeDefault: 0.0,
kCIAttributeIdentity: 250.0,
kCIAttributeClass: NSNumber.self,
kCIAttributeType: kCIAttributeTypeScalar],
"inputDelta": [kCIAttributeSliderMin: 0.0,
kCIAttributeSliderMax: 40.0,
kCIAttributeDefault: 20.0,
kCIAttributeIdentity: 20.0,
kCIAttributeClass: NSNumber.self,
kCIAttributeType: kCIAttributeTypeScalar],
"inputImage": [kCIAttributeClass: CIImage.self],
"outputImage": [kCIAttributeClass: CIImage.self]]
}
override var outputImage: CIImage? {
guard let image = inputImage else { print("No input image!"); return nil }
let inputImageExtent = image.extent
let extent = [inputImageExtent.origin.x, inputImageExtent.origin.y, inputImageExtent.size.width, inputImageExtent.size.height]
let definition = [inputImageExtent.origin.x, inputImageExtent.origin.y, inputImageExtent.size.width * inputScalingFactor, inputImageExtent.size.height * inputScalingFactor]
let sampler = CISampler(image: image)
let o = apply(genieKernel, arguments: [sampler, inputTime, inputDelta, inputTarget, inputScalingFactor], options: [kCIApplyOptionExtent: extent, kCIApplyOptionDefinition: definition])
return o
}
}
extension NSView {
var snapshot: CGImage? {
guard let bir = self.bitmapImageRepForCachingDisplay(in: self.bounds) else {
NSLog("No snapshot available.")
return nil
}
self.cacheDisplay(in: self.bounds, to: bir)
guard let inputImage = bir.cgImage else {
NSLog("Could not get cgImage from cached image.")
return nil
}
return inputImage
}
}
func performGenieAnimationIn(_ view: NSView, toLeftSideAtY target: CGFloat, using snapshot: CGImage) {
let didUseCoreImageFilters = view.layerUsesCoreImageFilters
view.layerUsesCoreImageFilters = true
// Configure the view to accept "our" layer.
assert(view.wantsLayer == true)
guard let rootLayer = view.layer else {
NSLog("No root layer for target view.")
view.layerUsesCoreImageFilters = didUseCoreImageFilters
return
}
rootLayer.contents = nil
let ourLayer = CALayer()
ourLayer.backgroundColor = NSColor.controlColor().cgColor
ourLayer.name = "contentsLayer"
ourLayer.bounds = view.bounds
ourLayer.anchorPoint = CGPoint(x: 0, y: 0)
ourLayer.isOpaque = false
ourLayer.contentsGravity = kCAGravityResize
ourLayer.position = CGPoint(x: 0, y: 0)
ourLayer.contents = snapshot
rootLayer.addSublayer(ourLayer)
let filter = GenieFilter()
filter.setDefaults()
filter.name = "genie"
filter.inputTarget = target
filter.inputTime = 2.0
filter.inputDelta = 10
filter.inputScalingFactor = view.window?.backingScaleFactor ?? 1
ourLayer.filters = [filter]
let animation = CABasicAnimation(keyPath: "filters.genie.inputTime")
animation.fromValue = 0
animation.toValue = 2
animation.duration = NSEvent.modifierFlags() == NSEventModifierFlags.option ? 4.0 : 0.4
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
CATransaction.begin()
CATransaction.setCompletionBlock {
ourLayer.removeFromSuperlayer()
if !didUseCoreImageFilters {
view.layerUsesCoreImageFilters = false
}
}
ourLayer.add(animation, forKey: "genie")
CATransaction.commit()
}
@omarojo
Copy link

omarojo commented May 17, 2017

any chance of porting this to iOS ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment