Last active
March 20, 2018 15:57
-
-
Save erikaderstedt/8257477c84f855bd67cf6dc45f129b77 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
any chance of porting this to iOS ?