├── .gitignore ├── Example-iOS ├── Example-iOS.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── mfathi.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── Example-iOS │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── Album.imageset │ │ ├── Album.pdf │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ └── Icon-Small@3x.png │ ├── Contents.json │ ├── drag.imageset │ │ ├── Contents.json │ │ └── drag.pdf │ ├── export.imageset │ │ ├── Contents.json │ │ └── export.pdf │ ├── flip.imageset │ │ ├── Contents.json │ │ └── flip.pdf │ ├── info.imageset │ │ ├── Contents.json │ │ └── info.pdf │ ├── test.imageset │ │ ├── Contents.json │ │ ├── frog1-1.jpg │ │ ├── frog1-2.jpg │ │ └── frog1.jpg │ ├── test2.imageset │ │ ├── Contents.json │ │ ├── DSC05600-1.jpg │ │ ├── DSC05600-2.jpg │ │ └── DSC05600.jpg │ └── test3.imageset │ │ ├── Contents.json │ │ ├── teapot-1.png │ │ ├── teapot-2.png │ │ └── teapot.png │ ├── Info.plist │ ├── Storyboards │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ └── ViewControllers │ ├── Filters │ ├── AddFilterViewController.swift │ ├── FilterGroupViewController.swift │ └── FiltersViewController.swift │ ├── Info │ ├── InfoViewController.swift │ └── MetadataFormatter.swift │ ├── Main │ └── MainViewController.swift │ ├── Settings │ ├── PickerCell.swift │ ├── SettingsCell.swift │ ├── SettingsViewController.swift │ └── ToggleCell.swift │ └── TestViewController.swift ├── MTLImage.podspec ├── MTLImage.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── mfathi.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── MTLImage ├── Info.plist ├── MTLImage.h ├── MTLImage.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── MTLImage.xcscheme │ └── xcuserdata │ │ └── mfathi.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── Sources │ ├── CloudKit │ └── CloudKitManager.swift │ ├── Core │ ├── BufferProvider.swift │ ├── Camera.swift │ ├── Context.swift │ ├── DataManager.swift │ ├── FPSCounter.swift │ ├── Filter.swift │ ├── FilterGroup.swift │ ├── Input.swift │ ├── MPS.swift │ ├── MTLImage.swift │ ├── MTLLib.swift │ ├── MTLObject.swift │ ├── MTLTypes.swift │ ├── Output.swift │ ├── Picture.swift │ ├── Property.swift │ └── View.swift │ ├── CoreData │ ├── FilterGroupRecord │ │ ├── MTLFilterGroupRecord+CoreDataProperties.swift │ │ └── MTLFilterGroupRecord.swift │ ├── FilterRecord │ │ ├── MTLFilterRecord+CoreDataProperties.swift │ │ └── MTLFilterRecord.swift │ ├── MTLImage.xcdatamodeld │ │ └── MTLImage.xcdatamodel │ │ │ └── contents │ └── PropertyRecord │ │ ├── MTLPropertyRecord+CoreDataProperties.swift │ │ └── MTLPropertyRecord.swift │ ├── Filters │ ├── Blend.swift │ ├── BoxBlur.swift │ ├── Brightness.swift │ ├── Buffer.swift │ ├── CannyEdgeDetection.swift │ ├── ColorMask.swift │ ├── ColorSelection.swift │ ├── Contrast.swift │ ├── Convolution.swift │ ├── Crop.swift │ ├── CrossHatch.swift │ ├── DataOutput.swift │ ├── DepthBlend.swift │ ├── DepthRenderer.swift │ ├── DepthToGrayscale.swift │ ├── Dilate.swift │ ├── Distortion.swift │ ├── Emboss.swift │ ├── Exposure.swift │ ├── GaussianBlur.swift │ ├── HSV.swift │ ├── HarrisCornerDetection.swift │ ├── Haze.swift │ ├── HighlightShadow.swift │ ├── Histogram.swift │ ├── HoughLineDetection.swift │ ├── Hue.swift │ ├── Invert.swift │ ├── Kuwahara.swift │ ├── LanczosScale.swift │ ├── LensFlare.swift │ ├── Levels.swift │ ├── LineDetection.swift │ ├── LowPass.swift │ ├── LuminanceThreshold.swift │ ├── Mask.swift │ ├── Mosaic.swift │ ├── NonMaximumSuppression.swift │ ├── PerlinNoise.swift │ ├── Pixellate.swift │ ├── PolkaDot.swift │ ├── Resize.swift │ ├── RollingAverage.swift │ ├── Saturation.swift │ ├── Scatter.swift │ ├── SelectiveHSL.swift │ ├── SelectiveHSL1.swift │ ├── Sharpen.swift │ ├── Sketch.swift │ ├── Smudge.swift │ ├── SobelEdgeDetection.swift │ ├── SobelEdgeDetectionThreshold.swift │ ├── Straighten.swift │ ├── Tent.swift │ ├── ToneCurve.swift │ ├── Toon.swift │ ├── Transform.swift │ ├── UnsharpMask.swift │ ├── Vignette.swift │ ├── Voronoi.swift │ ├── Water.swift │ ├── WeakPixelInclusion.swift │ ├── WhiteBalance.swift │ └── XYDerivative.swift │ ├── Shaders │ ├── Blend.metal │ ├── Brightness.metal │ ├── Camera.metal │ ├── ColorMask.metal │ ├── ColorSelection.metal │ ├── Contrast.metal │ ├── Convolution.metal │ ├── Crop.metal │ ├── CrossHatch.metal │ ├── DefaultShaders.metal │ ├── DepthBlend.metal │ ├── DepthMask.metal │ ├── DepthRenderer.metal │ ├── Distortion.metal │ ├── Exposure.metal │ ├── GaussianBlur.metal │ ├── HSV.metal │ ├── HarrisCornerDetection.metal │ ├── Haze.metal │ ├── HighlightShadow.metal │ ├── Histogram.metal │ ├── Hue.metal │ ├── Invert.metal │ ├── Kuwahara.metal │ ├── LensFlare.metal │ ├── Levels.metal │ ├── LineDetection.metal │ ├── LuminanceThreshold.metal │ ├── Makefile │ ├── Mask.metal │ ├── Mosaic.metal │ ├── NonMaximumSuppression.metal │ ├── OilPaint.metal │ ├── PerlinNoise.metal │ ├── Pixellate.metal │ ├── PolkaDot.metal │ ├── Resize.metal │ ├── RollingAverage.metal │ ├── Saturation.metal │ ├── Scatter.metal │ ├── SelectiveHSL.metal │ ├── Sharpen.metal │ ├── Sketch.metal │ ├── Smudge.metal │ ├── SobelEdgeDetection.metal │ ├── SobelEdgeDetectionThreshold.metal │ ├── Template.metal │ ├── ToneCurve.metal │ ├── Toon.metal │ ├── Transform.metal │ ├── UnsharpMask.metal │ ├── Vignette.metal │ ├── Voronoi.metal │ ├── Water.metal │ ├── WeakPixelInclusion.metal │ ├── WhiteBalance.metal │ ├── XYDerivative.metal │ └── default.metallib │ └── Tools │ ├── Extensions.swift │ ├── MTLTexture+UIImage.swift │ ├── Tools.swift │ ├── UIImage+MTLTexture.swift │ └── UIImage+Scale.swift └── Package.swift /.gitignore: -------------------------------------------------------------------------------- 1 | UserInterfaceState.xcuserstate 2 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS.xcodeproj/xcuserdata/mfathi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example-iOS.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example-iOS 4 | // 5 | // Created by Mohssen Fathi on 8/19/17. 6 | // Copyright © 2017 mohssenfathi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/Album.imageset/Album.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/Album.imageset/Album.pdf -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/Album.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Album.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-Small@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-Small@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-40@2x.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Icon-60@3x.png", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "1x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "20x20", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "29x29", 61 | "idiom" : "ipad", 62 | "filename" : "Icon-Small.png", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "Icon-Small@2x.png", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "size" : "40x40", 73 | "idiom" : "ipad", 74 | "filename" : "Icon-40.png", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "Icon-40@2x.png", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "size" : "76x76", 85 | "idiom" : "ipad", 86 | "filename" : "Icon-76.png", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "Icon-76@2x.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "size" : "83.5x83.5", 97 | "idiom" : "ipad", 98 | "filename" : "Icon-83.5@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "idiom" : "ios-marketing", 103 | "size" : "1024x1024", 104 | "scale" : "1x" 105 | } 106 | ], 107 | "info" : { 108 | "version" : 1, 109 | "author" : "xcode" 110 | } 111 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/drag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "drag.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/drag.imageset/drag.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/drag.imageset/drag.pdf -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/export.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "export.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/export.imageset/export.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/export.imageset/export.pdf -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/flip.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "flip.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/flip.imageset/flip.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/flip.imageset/flip.pdf -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/info.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "info.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/info.imageset/info.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/info.imageset/info.pdf -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "frog1-1.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "frog1.jpg", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "frog1-2.jpg", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1-1.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1-2.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test.imageset/frog1.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "DSC05600.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "DSC05600-1.jpg", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "DSC05600-2.jpg", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600-1.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600-2.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test2.imageset/DSC05600.jpg -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "teapot.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "teapot-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "teapot-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot-1.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot-2.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/Example-iOS/Example-iOS/Assets.xcassets/test3.imageset/teapot.png -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | NSPhotoLibraryUsageDescription 8 | $(PRODUCT_NAME) needs to access your photos for editing 9 | NSCameraUsageDescription 10 | $(PRODUCT_NAME) needs to access your camera to capture photos 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/Storyboards/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/Filters/AddFilterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddFilterViewController.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/9/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MTLImage 11 | 12 | protocol AddFilterViewControllerDelegate { 13 | func addFilterViewControllerDidSelectFilter(_ sender: AddFilterViewController, filter: MTLObject) 14 | } 15 | 16 | class AddFilterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 17 | 18 | @IBOutlet weak var tableView: UITableView! 19 | var delegate: AddFilterViewControllerDelegate? 20 | var filterNames = MTLImage.filters.sorted() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | navigationItem.title = "Add Filter" 26 | } 27 | 28 | @IBAction func closeButtonPressed(_ sender: AnyObject) { 29 | dismiss(animated: true, completion: nil) 30 | } 31 | 32 | 33 | // MARK: - UITableView 34 | // MARK: DataSource 35 | 36 | func numberOfSections(in tableView: UITableView) -> Int { 37 | return 1 38 | } 39 | 40 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 41 | return filterNames.count + 1 42 | } 43 | 44 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 45 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) 46 | cell.textLabel?.text = (indexPath.row == 0) ? "New Filter Group" : filterNames[indexPath.row - 1] 47 | return cell 48 | } 49 | 50 | 51 | // MARK: Delegate 52 | 53 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 54 | tableView.deselectRow(at: indexPath, animated: true) 55 | 56 | if indexPath.row == 0 { 57 | delegate?.addFilterViewControllerDidSelectFilter(self, filter: FilterGroup()) 58 | dismiss(animated: true, completion: nil) 59 | return 60 | } 61 | 62 | let title = filterNames[indexPath.row - 1] 63 | delegate?.addFilterViewControllerDidSelectFilter(self, filter: try! MTLImage.filter(title)!) 64 | dismiss(animated: true, completion: nil) 65 | } 66 | 67 | 68 | override func didReceiveMemoryWarning() { 69 | super.didReceiveMemoryWarning() 70 | } 71 | 72 | /* 73 | // MARK: - Navigation 74 | 75 | // In a storyboard-based application, you will often want to do a little preparation before navigation 76 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 77 | // Get the new view controller using segue.destinationViewController. 78 | // Pass the selected object to the new view controller. 79 | } 80 | */ 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/Info/InfoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InfoViewController.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/3/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class InfoViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | var metadata: [[String:Any]]! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | } 19 | 20 | @IBAction func closeButtonPressed(_ sender: AnyObject) { 21 | dismiss(animated: true, completion: nil) 22 | } 23 | 24 | 25 | // MARK: - UITableView 26 | // MARK: DataSource 27 | 28 | func numberOfSections(in tableView: UITableView) -> Int { 29 | return 1 30 | } 31 | 32 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 33 | return metadata.count 34 | } 35 | 36 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 37 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) 38 | 39 | let titleLabel = cell.viewWithTag(101) as! UILabel 40 | let valueLabel = cell.viewWithTag(102) as! UILabel 41 | 42 | let dict = metadata[(indexPath as NSIndexPath).row] as! [String : String] 43 | titleLabel.text = dict["title"]?.capitalized 44 | valueLabel.text = dict["value"] 45 | 46 | return cell 47 | } 48 | 49 | 50 | override func didReceiveMemoryWarning() { 51 | super.didReceiveMemoryWarning() 52 | } 53 | 54 | 55 | /* 56 | // MARK: - Navigation 57 | 58 | // In a storyboard-based application, you will often want to do a little preparation before navigation 59 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 60 | // Get the new view controller using segue.destinationViewController. 61 | // Pass the selected object to the new view controller. 62 | } 63 | */ 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/Settings/PickerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PickerCell.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/14/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol PickerCellDelegate { 12 | func pickerCellDidSelectItem(_ sender: PickerCell, index: Int) 13 | } 14 | 15 | class PickerCell: UITableViewCell, UIPickerViewDataSource, UIPickerViewDelegate { 16 | 17 | @IBOutlet var picker: UIPickerView! 18 | @IBOutlet var titleLabel: UILabel! 19 | var selectionItems = [Int : String]() { 20 | didSet { 21 | picker.reloadAllComponents() 22 | } 23 | } 24 | var delegate: PickerCellDelegate? 25 | 26 | override func awakeFromNib() { 27 | super.awakeFromNib() 28 | } 29 | 30 | override func setSelected(_ selected: Bool, animated: Bool) { 31 | super.setSelected(selected, animated: animated) 32 | } 33 | 34 | 35 | // MARK: - UIPickerView 36 | // MARK: DataSource 37 | 38 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 39 | return 1 40 | } 41 | 42 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 43 | return selectionItems.count 44 | } 45 | 46 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 47 | return selectionItems[row] 48 | } 49 | 50 | // MARK: Delegate 51 | 52 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 53 | delegate?.pickerCellDidSelectItem(self, index: row) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/Settings/SettingsCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsCell.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/31/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol SettingsCellDelegate { 12 | func settingsCellSliderValueChanged(_ sender: SettingsCell, value: Float) 13 | } 14 | 15 | class SettingsCell: UITableViewCell { 16 | 17 | @IBOutlet weak var slider: UISlider! 18 | @IBOutlet weak var titleLabel: UILabel! 19 | @IBOutlet weak var valueLabel: UILabel! 20 | @IBOutlet weak var messageLabel: UILabel! 21 | 22 | lazy var gradient: CAGradientLayer? = { 23 | return self.trackGradient() 24 | }() 25 | 26 | var message: String? { 27 | didSet { 28 | if message == "" || message == nil { 29 | messageLabel.isHidden = true 30 | messageLabel.text = "" 31 | } else { 32 | messageLabel.isHidden = false 33 | messageLabel.text = message 34 | } 35 | } 36 | } 37 | 38 | var spectrum: Bool = false { 39 | didSet { 40 | if spectrum == true { 41 | slider.minimumTrackTintColor = UIColor.clear 42 | slider.maximumTrackTintColor = UIColor.clear 43 | slider.thumbTintColor = currentColor() 44 | layer.insertSublayer(gradient!, at: 0) 45 | layoutSubviews() 46 | } 47 | } 48 | } 49 | 50 | var delegate: SettingsCellDelegate? 51 | 52 | override func awakeFromNib() { 53 | super.awakeFromNib() 54 | } 55 | 56 | override func layoutSubviews() { 57 | super.layoutSubviews() 58 | if spectrum == true { 59 | gradient?.frame = CGRect(x: 25, y: slider.center.y - 1.0, width: frame.width - 100, height: 2.0) 60 | } 61 | } 62 | 63 | override func setSelected(_ selected: Bool, animated: Bool) { 64 | super.setSelected(selected, animated: animated) 65 | } 66 | 67 | @IBAction func sliderValueChanged(_ sender: UISlider) { 68 | delegate?.settingsCellSliderValueChanged(self, value: sender.value) 69 | 70 | if spectrum { 71 | slider.thumbTintColor = currentColor() 72 | } 73 | } 74 | 75 | func currentColor() -> UIColor { 76 | return UIColor(hue: CGFloat(slider.value) * (5.0/6.0) + (1.0/6.0), saturation: 1.0, brightness: 1.0, alpha: 1.0) 77 | } 78 | 79 | func trackGradient() -> CAGradientLayer { 80 | let gradientLayer = CAGradientLayer() 81 | 82 | var colors = [CGColor]() 83 | var locations = [NSNumber]() 84 | for i in 1 ..< 7 { 85 | colors.append(UIColor(hue: (1.0/6.0) * CGFloat(i), saturation: 1.0, brightness: 1.0, alpha: 1.0).cgColor) 86 | locations.append(NSNumber(value: (1.0/6.0) * Float(i))) 87 | } 88 | 89 | gradientLayer.colors = colors 90 | gradientLayer.locations = locations 91 | gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) 92 | gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) 93 | 94 | return gradientLayer 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/Settings/ToggleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToggleCell.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 9/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ToggleCellDelegate { 12 | func toggleValueChanged(sender: ToggleCell, isOn: Bool) 13 | } 14 | 15 | class ToggleCell: UITableViewCell { 16 | 17 | @IBOutlet weak var toggleSwitch: UISwitch! 18 | @IBOutlet weak var valueLabel: UILabel! 19 | @IBOutlet weak var titleLabel: UILabel! 20 | 21 | var delegate: ToggleCellDelegate? 22 | 23 | override func awakeFromNib() { 24 | super.awakeFromNib() 25 | } 26 | 27 | override func setSelected(_ selected: Bool, animated: Bool) { 28 | super.setSelected(selected, animated: animated) 29 | } 30 | 31 | @IBAction func switchToggled(_ sender: UISwitch) { 32 | valueLabel.text = sender.isOn ? "On" : "Off" 33 | delegate?.toggleValueChanged(sender: self, isOn: sender.isOn) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example-iOS/Example-iOS/ViewControllers/TestViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestViewController.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/25/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MTLImage 11 | 12 | class TestViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | // let _ = MTLFeedForwardNeuralNetwork([2, 4, 3, 2]) 18 | 19 | } 20 | 21 | override func didReceiveMemoryWarning() { 22 | super.didReceiveMemoryWarning() 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /MTLImage.podspec: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Be sure to run `pod lib lint MTLImage.podspec' to ensure this is a 4 | # valid spec before submitting. 5 | # 6 | # Any lines starting with a # are optional, but their use is encouraged 7 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 8 | # 9 | 10 | Pod::Spec.new do |s| 11 | s.name = "MTLImage" 12 | s.version = "0.0.1" 13 | s.summary = "Image processing framework built on top of Metal" 14 | 15 | s.description = <<-DESC 16 | A framework to simplify data processing on the GPU using Metal. 17 | DESC 18 | 19 | s.homepage = "https://github.com/mohssenfathi/MTLImage" 20 | s.license = 'MIT' 21 | s.author = { "mohssenfathi" => "mmohssenfathi@gmail.com" } 22 | s.source = { :git => "https://github.com/mohssenfathi/MTLImage.git", :tag => s.version.to_s } 23 | 24 | s.ios.deployment_target = '10.0' 25 | s.tvos.deployment_target = '10.0' 26 | s.requires_arc = true 27 | 28 | s.source_files = 'MTLImage/Sources/**/*.{swift, m, h, mm, hpp, cpp, c}' 29 | s.resources = ['MTLImage/Sources/CoreData/*.xcdatamodeld', 'MTLImage/Sources/Shaders/*.metallib'] 30 | 31 | # Unused for now 32 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 33 | # s.social_media_url = 'https://twitter.com/' 34 | # s.public_header_files = 'Pod/Classes/**/*.h' 35 | 36 | end 37 | -------------------------------------------------------------------------------- /MTLImage.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MTLImage.xcworkspace/xcuserdata/mfathi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /MTLImage/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSCameraUsageDescription 6 | $(PRODUCT_NAME) needs to access your camera to capture photos 7 | NSPhotoLibraryUsageDescription 8 | $(PRODUCT_NAME) needs to access your photos for editing 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | FMWK 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /MTLImage/MTLImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLImage.h 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 6/16/17. 6 | // Copyright © 2017 Mohssen Fathi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for MTLImage. 12 | FOUNDATION_EXPORT double MTLImageVersionNumber; 13 | 14 | //! Project version string for MTLImage. 15 | FOUNDATION_EXPORT const unsigned char MTLImageVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | -------------------------------------------------------------------------------- /MTLImage/MTLImage.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MTLImage/MTLImage.xcodeproj/xcuserdata/mfathi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MTLImage.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 45AA0BE51F490B460091DBB0 16 | 17 | primary 18 | 19 | 20 | 45AA0BEE1F490B460091DBB0 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /MTLImage/Sources/CloudKit/CloudKitManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CloudKitManager.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/3/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import CloudKit 11 | 12 | let publicDatabase = CKContainer.default().publicCloudDatabase 13 | 14 | public 15 | class CloudKitManager: NSObject { 16 | 17 | static let sharedManager = CloudKitManager() 18 | 19 | func allRecords() -> [CKRecord]? { 20 | 21 | return nil 22 | } 23 | 24 | func upload(_ filterGroup: FilterGroup, container: CKContainer, completion: ((_ record: CKRecord?, _ error: Error?) -> ())?) { 25 | 26 | let record = filterGroup.ckRecord() 27 | 28 | container.publicCloudDatabase.save(record) { (record, error) in 29 | completion?(record, error) 30 | } 31 | } 32 | 33 | } 34 | 35 | public 36 | extension FilterGroup { 37 | 38 | public func ckRecord() -> CKRecord { 39 | 40 | let record = CKRecord(recordType: "FilterGroup") 41 | 42 | record["identifier"] = self.identifier as CKRecordValue 43 | record["title"] = self.title as CKRecordValue 44 | record["category"] = self.category as CKRecordValue 45 | record["description"] = self.filterDescription as CKRecordValue 46 | record["filterData"] = filterDataAsset(MTLImage.archive(self)!) 47 | 48 | return record 49 | } 50 | 51 | func filterDataAsset(_ data: Data) -> CKAsset { 52 | 53 | let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first 54 | let url = URL(fileURLWithPath: path!).appendingPathComponent(identifier) 55 | try! data.write(to: url, options: .atomicWrite) // Handle later 56 | 57 | return CKAsset(fileURL: url) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/BufferProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BufferProvider.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 9/10/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class BufferProvider: NSObject { 12 | 13 | let inflightBuffersCount: Int = 3 14 | private var uniformsBuffers: [MTLBuffer] 15 | private var avaliableBufferIndex: Int = 0 16 | var bufferSize: Int = 0 17 | 18 | init(device:MTLDevice, bufferSize: Int) { 19 | 20 | uniformsBuffers = [MTLBuffer]() 21 | self.bufferSize = bufferSize 22 | 23 | for _ in 0 ..< inflightBuffersCount { 24 | if let uniformsBuffer = device.makeBuffer(length: bufferSize, options: []) { 25 | uniformsBuffers.append(uniformsBuffer) 26 | } 27 | } 28 | } 29 | 30 | func nextBuffer(uniforms: UnsafeRawPointer) -> MTLBuffer { 31 | 32 | let buffer = uniformsBuffers[avaliableBufferIndex] 33 | let bufferPointer = buffer.contents() 34 | 35 | memcpy(bufferPointer, uniforms, bufferSize) 36 | // memcpy(bufferPointer, modelViewMatrix.raw(), bufferSize) 37 | // memcpy(bufferPointer + sizeof(Float)*Matrix4.numberOfElements(), projectionMatrix.raw(), sizeof(Float)*Matrix4.numberOfElements()) 38 | 39 | avaliableBufferIndex = (avaliableBufferIndex + 1) % inflightBuffersCount 40 | 41 | return buffer 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLContext.swift 3 | // Pods 4 | // 5 | // Created by Mohammad Fathi on 3/10/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | // Set to true to use compiled shaders 12 | let useMetalib = false 13 | 14 | public 15 | class Context: NSObject { 16 | 17 | var device: MTLDevice! 18 | var commandQueue: MTLCommandQueue! 19 | var processingSize: CGSize! 20 | var processingQueue: DispatchQueue! 21 | var needsUpdate: Bool = true 22 | let semaphore = DispatchSemaphore(value: 3) 23 | 24 | var source: Input? 25 | var output: Output? 26 | 27 | private var internalLibrary: MTLLibrary! 28 | var library: MTLLibrary! { 29 | get { 30 | if internalLibrary == nil { 31 | loadLibrary() 32 | } 33 | return internalLibrary 34 | } 35 | } 36 | 37 | deinit { 38 | source = nil 39 | output = nil 40 | } 41 | 42 | override init() { 43 | super.init() 44 | 45 | device = MTLCreateSystemDefaultDevice() 46 | guard MTLImage.isMetalSupported else { return } 47 | 48 | loadLibrary() 49 | 50 | self.commandQueue = self.device.makeCommandQueue() 51 | self.processingQueue = DispatchQueue(label: "MTLImageProcessQueue") 52 | // DispatchQueue(label: "MTLImageProcessQueue", attributes: DispatchQueueAttributes.concurrent) 53 | 54 | refreshCurrentCommandBuffer() 55 | } 56 | 57 | func loadLibrary() { 58 | if useMetalib { 59 | internalLibrary = MTLLib.sharedLibrary(device: device) 60 | assert(internalLibrary != nil) 61 | } 62 | else { 63 | if #available(iOS 10.0, *) { 64 | internalLibrary = try? device.makeDefaultLibrary(bundle: Bundle(for: Camera.self)) 65 | } else { 66 | // This shouldn't be used in production. If it is, user might need to add an empty shader to parent project 67 | internalLibrary = device.makeDefaultLibrary() 68 | } 69 | } 70 | } 71 | 72 | 73 | /* Returns the full filter chain not including source and output (only first targets for now) 74 | TODO: Include filters with multiple targets 75 | */ 76 | var filterChain: [MTLObject] { 77 | 78 | guard let source = source else { 79 | return [] 80 | } 81 | 82 | var chain = [MTLObject]() 83 | var object = source.targets.first as? MTLObject 84 | 85 | while object != nil { 86 | chain.append(object!) 87 | object = object?.targets.first as? MTLObject 88 | } 89 | 90 | return chain 91 | } 92 | 93 | var currentCommandBuffer: MTLCommandBuffer! 94 | func refreshCurrentCommandBuffer() { 95 | currentCommandBuffer = commandQueue.makeCommandBuffer() 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/Input.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Input.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/26/17. 6 | // 7 | 8 | import Metal 9 | 10 | public protocol Input { 11 | 12 | var texture: MTLTexture? { get } 13 | var context: Context { get } 14 | var device : MTLDevice { get } 15 | var targets: [Output] { get } 16 | 17 | var title : String { get set } 18 | var identifier : String { get set } 19 | var needsUpdate: Bool { get set } 20 | var continuousUpdate: Bool { get } 21 | 22 | func addTarget(_ target: Output) 23 | func removeTarget(_ target: Output) 24 | func removeAllTargets() 25 | func processIfNeeded() 26 | } 27 | 28 | public extension Input { 29 | 30 | public func processIfNeeded() { } 31 | public mutating func setNeedsUpdate() { 32 | needsUpdate = true 33 | } 34 | 35 | var destinations: [Output] { 36 | var dests = [Output]() 37 | 38 | if targets.count == 0, let out = self as? Output { 39 | dests.append(out) 40 | } 41 | 42 | for target in targets { 43 | if let obj = target as? MTLObject { 44 | dests.append(contentsOf: obj.destinations) 45 | } else { 46 | dests.append(target) 47 | } 48 | } 49 | 50 | return dests 51 | } 52 | 53 | // func removeDuplicates(in array: [Output]) -> [Output] { 54 | // 55 | // var identifiers = Set(array.map({ $0.identifier })) 56 | // 57 | // var new = [Output]() 58 | // for output in array { 59 | // if identifiers.contains(output.identifier) { 60 | // new.append(output) 61 | // identifiers.remove(output.identifier) 62 | // } 63 | // } 64 | // 65 | // return new 66 | // } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/MPS.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // MPS.swift 4 | // Pods 5 | // 6 | // Created by Mohssen Fathi on 6/17/16. 7 | // 8 | // 9 | 10 | import UIKit 11 | import MetalPerformanceShaders 12 | 13 | public 14 | class MPS: Filter { 15 | 16 | var kernel: MPSKernel! 17 | 18 | public override init(functionName: String?) { 19 | super.init(functionName: nil) 20 | } 21 | 22 | required public init?(coder aDecoder: NSCoder) { 23 | super.init(coder: aDecoder) 24 | } 25 | 26 | public override var needsUpdate: Bool { 27 | didSet { 28 | if needsUpdate == true { 29 | for target in targets { 30 | if var object = target as? MTLObject { 31 | object.setNeedsUpdate() 32 | } 33 | else if let view = target as? View { 34 | view.setNeedsDisplay() 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | public override func process() { 42 | 43 | if texture == nil { 44 | initTexture() 45 | } 46 | 47 | input?.processIfNeeded() 48 | 49 | guard let inputTexture = input?.texture, 50 | let texture = texture, 51 | let commandBuffer = context.commandQueue.makeCommandBuffer() else { return } 52 | 53 | autoreleasepool { 54 | 55 | commandBuffer.label = "Filter: " + title 56 | 57 | (kernel as? MPSUnaryImageKernel)?.encode(commandBuffer: commandBuffer, sourceTexture: inputTexture, destinationTexture: texture) 58 | 59 | configureCommandBuffer(commandBuffer) 60 | 61 | commandBuffer.addCompletedHandler({ (commandBuffer) in 62 | self.needsUpdate = false 63 | }) 64 | 65 | commandBuffer.commit() 66 | commandBuffer.waitUntilCompleted() 67 | } 68 | } 69 | 70 | func configureCommandBuffer(_ commandBuffer: MTLCommandBuffer) { 71 | // Needs to be subclassed 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/MTLLib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLLibrary.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 9/12/16. 6 | // 7 | // 8 | 9 | import Metal 10 | 11 | public 12 | class MTLLib: NSObject { 13 | 14 | static let sharedLib = MTLLib() 15 | 16 | var library: MTLLibrary! = nil 17 | 18 | // TODO: Make throw 19 | class func sharedLibrary(device: MTLDevice) -> MTLLibrary? { 20 | 21 | if MTLLib.sharedLib.library == nil { 22 | let bundle = Bundle(for: MTLImage.classForCoder()) 23 | guard let path = bundle.path(forResource: "default", ofType: "metallib") else { 24 | print("Cannot find metallib") 25 | return nil 26 | } 27 | 28 | do { 29 | MTLLib.sharedLib.library = try device.makeLibrary(filepath: path) 30 | } 31 | catch { 32 | print("Error creating metallib") 33 | return nil 34 | } 35 | } 36 | 37 | return MTLLib.sharedLib.library 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/MTLTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLTypes.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | typealias MTLFloat = Float32 12 | 13 | public struct MTLFloat2 { 14 | var one : MTLFloat 15 | var two : MTLFloat 16 | } 17 | 18 | public struct MTLFloat3 { 19 | var one : MTLFloat 20 | var two : MTLFloat 21 | var three: MTLFloat 22 | } 23 | 24 | public struct MTLFloat4 { 25 | var one : MTLFloat 26 | var two : MTLFloat 27 | var three: MTLFloat 28 | var four : MTLFloat 29 | }; 30 | 31 | public struct MTLFloat3x3 { 32 | var one : MTLFloat3 33 | var two : MTLFloat3 34 | var three: MTLFloat3 35 | } 36 | 37 | public struct MTLFloat4x4 { 38 | var one : MTLFloat4 39 | var two : MTLFloat4 40 | var three: MTLFloat4 41 | var four : MTLFloat4 42 | } 43 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/Output.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Output.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/26/17. 6 | // 7 | 8 | public protocol Output { 9 | 10 | var input : Input? { get set } 11 | var title : String { get set } 12 | var identifier: String { get set } 13 | } 14 | 15 | public extension Output { 16 | 17 | public var source: Input? { 18 | get { 19 | var inp: Input? = input 20 | while inp != nil { 21 | 22 | if let sourcePicture = inp as? Picture { 23 | return sourcePicture 24 | } 25 | 26 | #if !os(tvOS) 27 | if let camera = inp as? Camera { 28 | return camera 29 | } 30 | #endif 31 | 32 | if inp is Output { 33 | inp = (inp as? Output)?.input 34 | } 35 | } 36 | return nil 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /MTLImage/Sources/Core/Property.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Property.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | public 12 | class Property: NSObject, NSCoding { 13 | 14 | public 15 | enum PropertyType: Int { 16 | case value = 0, 17 | bool, 18 | point, 19 | rect, 20 | color, 21 | selection, 22 | image 23 | } 24 | 25 | public var title: String! 26 | public var key: String! 27 | // public var keyPath: AnyKeyPath! 28 | public var propertyType: PropertyType! 29 | public var minimumValue: Float = 0.0 30 | public var defaultValue: Float = 0.5 31 | public var maximumValue: Float = 1.0 32 | public var selectionItems: [Int : String]? 33 | 34 | init(key: String, title: String) { 35 | super.init() 36 | self.key = key 37 | self.title = title 38 | self.propertyType = .value 39 | } 40 | 41 | init(key: String, title: String, propertyType: PropertyType) { 42 | super.init() 43 | self.key = key 44 | self.title = title 45 | self.propertyType = propertyType 46 | } 47 | 48 | 49 | 50 | // MARK: - NSCoding 51 | 52 | public func encode(with aCoder: NSCoder) { 53 | aCoder.encode(title , forKey: "title") 54 | aCoder.encode(key, forKey: "key") 55 | aCoder.encode(minimumValue, forKey: "minimumValue") 56 | aCoder.encode(maximumValue, forKey: "maximumValue") 57 | aCoder.encode(defaultValue, forKey: "defaultValue") 58 | aCoder.encode(selectionItems, forKey: "selectionItems") 59 | aCoder.encode(propertyType.rawValue, forKey: "propertType") 60 | } 61 | 62 | required public init?(coder aDecoder: NSCoder) { 63 | super.init() 64 | key = aDecoder.decodeObject(forKey: "key") as! String 65 | title = aDecoder.decodeObject(forKey: "title") as! String 66 | minimumValue = aDecoder.decodeFloat(forKey: "minimumValue") 67 | maximumValue = aDecoder.decodeFloat(forKey: "maximumValue") 68 | defaultValue = aDecoder.decodeFloat(forKey: "defaultValue") 69 | selectionItems = aDecoder.decodeObject(forKey: "selectionItems") as? [Int: String] 70 | propertyType = PropertyType(rawValue: aDecoder.decodeInteger(forKey: "propertyType")) 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/FilterGroupRecord/MTLFilterGroupRecord+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLFilterGroupRecord+CoreDataProperties.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension MTLFilterGroupRecord { 16 | 17 | @NSManaged var identifier: String? 18 | @NSManaged var title: String? 19 | @NSManaged var filters: NSOrderedSet? 20 | 21 | } 22 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/FilterGroupRecord/MTLFilterGroupRecord.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLFilterGroupRecord.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | 13 | class MTLFilterGroupRecord: NSManagedObject { 14 | 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/FilterRecord/MTLFilterRecord+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLFilterRecord+CoreDataProperties.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension MTLFilterRecord { 16 | 17 | @NSManaged var identifier: String? 18 | @NSManaged var functionName: String? 19 | @NSManaged var title: String? 20 | @NSManaged var index: NSNumber? 21 | @NSManaged var properties: NSOrderedSet? 22 | @NSManaged var host: MTLFilterGroupRecord? 23 | 24 | } 25 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/FilterRecord/MTLFilterRecord.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLFilterRecord.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | 13 | class MTLFilterRecord: NSManagedObject { 14 | 15 | // Insert code here to add functionality to your managed object subclass 16 | 17 | } 18 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/PropertyRecord/MTLPropertyRecord+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLPropertyRecord+CoreDataProperties.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension MTLPropertyRecord { 16 | 17 | @NSManaged var defaultValue: NSNumber? 18 | @NSManaged var key: String? 19 | @NSManaged var maximumValue: NSNumber? 20 | @NSManaged var minimumValue: NSNumber? 21 | @NSManaged var title: String? 22 | @NSManaged var type: String? 23 | @NSManaged var propertyType: NSNumber? 24 | @NSManaged var host: MTLFilterRecord? 25 | 26 | // Property Types 27 | @NSManaged var value: NSNumber? 28 | @NSManaged var bool: NSNumber? // Test this 29 | @NSManaged var point: NSValue? 30 | @NSManaged var rect: NSValue? 31 | @NSManaged var color: UIColor? 32 | @NSManaged var selectionItems: [Int: String]? 33 | @NSManaged var image: Data? 34 | } 35 | -------------------------------------------------------------------------------- /MTLImage/Sources/CoreData/PropertyRecord/MTLPropertyRecord.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLPropertyRecord.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/10/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | @objc(MTLPropertyRecord) 13 | class MTLPropertyRecord: NSManagedObject { 14 | 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/BoxBlur.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoxConvolution.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/18/16. 6 | // 7 | // 8 | 9 | import MetalPerformanceShaders 10 | 11 | public 12 | class BoxBlur: MPS { 13 | 14 | @objc var radius: Float = 0.5 { 15 | didSet { 16 | clamp(&radius, low: 0, high: 1) 17 | kernel = MPSImageBox(device : context.device, 18 | kernelWidth : Tools.odd(Int(radius * 80.0)), 19 | kernelHeight: Tools.odd(Int(radius * 80.0))) 20 | (kernel as! MPSImageBox).edgeMode = .clamp 21 | needsUpdate = true 22 | } 23 | } 24 | 25 | 26 | init() { 27 | super.init(functionName: nil) 28 | commonInit() 29 | } 30 | 31 | override init(functionName: String?) { 32 | super.init(functionName: nil) 33 | commonInit() 34 | } 35 | 36 | func commonInit() { 37 | kernel = MPSImageBox(device : context.device, 38 | kernelWidth : Tools.odd(Int(radius * 80.0)), 39 | kernelHeight: Tools.odd(Int(radius * 80.0))) 40 | (kernel as! MPSImageBox).edgeMode = .clamp 41 | 42 | title = "Box Blur" 43 | properties = [Property(key: "radius" , title: "Radius")] 44 | } 45 | 46 | required public init?(coder aDecoder: NSCoder) { 47 | super.init(coder: aDecoder) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Brightness.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Brightness.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // 7 | // 8 | 9 | struct BrightnessUniforms: Uniforms { 10 | var brightness: Float = 0.5; 11 | } 12 | 13 | public 14 | class Brightness: Filter { 15 | 16 | var uniforms = BrightnessUniforms() 17 | 18 | @objc public var brightness: Float = 0.5 { 19 | didSet { 20 | clamp(&brightness, low: 0, high: 1) 21 | needsUpdate = true 22 | } 23 | } 24 | 25 | public init() { 26 | super.init(functionName: "brightness") 27 | title = "Brightness" 28 | properties = [Property(key: "brightness", title: "Brightness")] 29 | // properties = [Property(keyPath: \Brightness.brightness, title: "Brightness")] 30 | update() 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | override func update() { 38 | if self.input == nil { return } 39 | uniforms.brightness = brightness * 2.0 - 1.0 40 | updateUniforms(uniforms: uniforms) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Buffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Buffer.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/15/17. 6 | // 7 | 8 | public 9 | class Buffer: Filter { 10 | 11 | var textureQueue = [MTLTexture]() 12 | 13 | public init() { 14 | super.init(functionName: "EmptyShader") 15 | title = "Buffer" 16 | properties = [] 17 | } 18 | 19 | required public init?(coder aDecoder: NSCoder) { 20 | super.init(coder: aDecoder) 21 | } 22 | 23 | public override func process() { 24 | 25 | guard let texture = input?.texture?.copy(device: device) else { return } 26 | 27 | textureQueue.insert(texture, at: 0) 28 | 29 | if textureQueue.count > bufferLength { 30 | self.texture = textureQueue.popLast() 31 | } 32 | } 33 | 34 | // MARK: - Properties 35 | public var bufferLength: Int = 10 { 36 | didSet { needsUpdate = true } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/CannyEdgeDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CannyEdgeDetectionGroup.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class CannyEdgeDetection: FilterGroup { 12 | 13 | let saturationFilter = Saturation() 14 | let edgeDetectionFilter = SobelEdgeDetection() 15 | let blurFilter = GaussianBlur() 16 | let nonMaximumSuppressionFilter = NonMaximumSuppression() 17 | let weakPixelInclusion = WeakPixelInclusion() 18 | 19 | override init() { 20 | super.init() 21 | title = "Canny Edge Detection" 22 | 23 | saturationFilter.saturation = 0.0 24 | blurFilter.sigma = 0.1 25 | edgeDetectionFilter.edgeStrength = 0.1 26 | nonMaximumSuppressionFilter.lowerThreshold = 0.1 27 | nonMaximumSuppressionFilter.upperThreshold = 0.4 28 | 29 | add(saturationFilter) 30 | add(blurFilter) 31 | add(edgeDetectionFilter) 32 | add(nonMaximumSuppressionFilter) 33 | add(weakPixelInclusion) 34 | } 35 | 36 | required internal init?(coder aDecoder: NSCoder) { 37 | super.init(coder: aDecoder) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/ColorMask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorMask.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/29/17. 6 | // 7 | 8 | struct ColorMaskUniforms { 9 | var r: Float = 0.0 10 | var g: Float = 0.0 11 | var b: Float = 0.0 12 | var a: Float = 0.0 13 | var threshold: Float = 1.0 14 | } 15 | 16 | public 17 | class ColorMask: Filter { 18 | 19 | public var color: UIColor = .clear 20 | public var threshold: Float = 1.0 21 | 22 | var uniforms = ColorMaskUniforms() 23 | 24 | override public var continuousUpdate: Bool { 25 | return true 26 | } 27 | 28 | public init() { 29 | super.init(functionName: "colorMask") 30 | 31 | title = "Color Mask" 32 | properties = [ 33 | Property(key: "color", title: "Color", propertyType: .color), 34 | Property(key: "threshold", title: "Threshold") 35 | ] 36 | } 37 | 38 | public required init?(coder aDecoder: NSCoder) { 39 | super.init(coder: aDecoder) 40 | } 41 | 42 | override func update() { 43 | super.update() 44 | 45 | uniforms.threshold = threshold 46 | 47 | if color.cgColor.numberOfComponents == 4, let components = color.cgColor.components { 48 | uniforms.r = Float(components[0]) 49 | uniforms.g = Float(components[1]) 50 | uniforms.b = Float(components[2]) 51 | uniforms.a = Float(components[3]) 52 | } else { 53 | uniforms.r = 0.0 54 | uniforms.g = 0.0 55 | uniforms.b = 0.0 56 | uniforms.a = 0.0 57 | } 58 | 59 | uniformsBuffer = context.device.makeBuffer(bytes: &uniforms, 60 | length: MemoryLayout.size, 61 | options: .cpuCacheModeWriteCombined) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Contrast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Contrast.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct ContrastUniforms: Uniforms { 12 | var contrast: Float = 0.5; 13 | } 14 | 15 | public 16 | class Contrast: Filter { 17 | var uniforms = ContrastUniforms() 18 | 19 | @objc public var contrast: Float = 0.5 { 20 | didSet { 21 | clamp(&contrast, low: 0, high: 1) 22 | needsUpdate = true 23 | } 24 | } 25 | 26 | public init() { 27 | super.init(functionName: "contrast") 28 | title = "Contrast" 29 | properties = [Property(key: "contrast", title: "Contrast")] 30 | update() 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | override func update() { 38 | if input == nil { return } 39 | uniforms.contrast = Tools.convert(contrast, oldMin: 0.0, oldMid: 0.5, oldMax: 1.0, newMin: 0.0, newMid: 1.0, newMax: 4.0) 40 | updateUniforms(uniforms: uniforms) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Convolution.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Convolution.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | public 12 | class Convolution: Filter { 13 | 14 | private var convolutionMatrixTexture: MTLTexture? 15 | 16 | @objc public var convolutionMatrix: [[Float]] = [[0.0, 0.0, 0.0], 17 | [0.0, 1.0, 0.0], 18 | [0.0, 0.0, 0.0]] { 19 | didSet { 20 | needsUpdate = true 21 | convolutionMatrixTexture = nil 22 | } 23 | } 24 | 25 | public init() { 26 | super.init(functionName: "convolution") 27 | title = "Convolution" 28 | properties = [] 29 | 30 | update() 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 38 | if convolutionMatrixTexture == nil { 39 | let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r32Float, width: 3, height: 3, mipmapped: false) 40 | convolutionMatrixTexture = device.makeTexture(descriptor: textureDescriptor) 41 | 42 | let f = Array(convolutionMatrix.joined()) 43 | convolutionMatrixTexture!.replace(region: MTLRegionMake2D(0, 0, 3, 3), mipmapLevel: 0, withBytes: f, bytesPerRow: MemoryLayout.size * 3) 44 | } 45 | commandEncoder.setTexture(convolutionMatrixTexture, index: 2) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/CrossHatch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.swift 3 | // Pods 4 | // 5 | // Created by Mohammad Fathi on 3/10/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct CrossHatchUniforms: Uniforms { 12 | var crossHatchSpacing: Float = 0.03; 13 | var lineWidth: Float = 0.003; 14 | } 15 | 16 | public 17 | class CrossHatch: Filter { 18 | 19 | var uniforms = CrossHatchUniforms() 20 | 21 | @objc public var crossHatchSpacing: Float = 0.5 { 22 | didSet { 23 | clamp(&crossHatchSpacing, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var lineWidth: Float = 0.5 { 29 | didSet { 30 | clamp(&lineWidth, low: 0, high: 1) 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "crossHatch") 37 | title = "Cross Hatch" 38 | properties = [ Property(key: "crossHatchSpacing", title: "Cross Hatch Spacing"), 39 | Property(key: "lineWidth" , title: "Line Width" )] 40 | update() 41 | } 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | super.init(coder: aDecoder) 45 | } 46 | 47 | override func update() { 48 | 49 | // guard input != nil else { 50 | // needsUpdate = false 51 | // return 52 | // } 53 | // 54 | // guard texture != nil else { 55 | // needsUpdate = false 56 | // return 57 | // } 58 | 59 | // var chs = Tools.convert(crossHatchSpacing, oldMin: 0, oldMax: 1, newMin: 0.01, newMax: 0.08) 60 | 61 | // if uniformsBuffer != nil { 62 | // var singlePixelSpacing: Float! 63 | // if texture!.width != 0 { singlePixelSpacing = 1.0 / Float(texture!.width) } 64 | // else { singlePixelSpacing = 1.0 / 2048.0 } 65 | // if (chs < singlePixelSpacing) { chs = singlePixelSpacing } 66 | // } 67 | 68 | uniforms.crossHatchSpacing = Tools.convert(crossHatchSpacing, oldMin: 0, oldMax: 1, newMin: 20.0, newMax: 100.0) 69 | // uniforms.lineWidth = Tools.convert(lineWidth, oldMin: 0, oldMax: 1, newMin: 0.001, newMax: 0.008) 70 | uniforms.lineWidth = Tools.convert(lineWidth, oldMin: 0, oldMax: 1, newMin: 1.0, newMax: 8.0) 71 | updateUniforms(uniforms: uniforms) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/DataOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataOutput.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/28/17. 6 | // 7 | 8 | import UIKit 9 | 10 | public 11 | class DataOutput: Filter { 12 | 13 | public var newDataAvailable: ((_ data: [UInt8]) -> ())? 14 | 15 | public init() { 16 | super.init(functionName: nil) 17 | title = "Data Output" 18 | properties = [] 19 | } 20 | 21 | public required init?(coder aDecoder: NSCoder) { 22 | super.init(coder: aDecoder) 23 | } 24 | 25 | private var dataBuffer: MTLBuffer! 26 | 27 | public override func process() { 28 | 29 | input?.processIfNeeded() 30 | if texture == nil { initTexture() } 31 | 32 | texture = input?.texture 33 | 34 | guard let texture = texture, 35 | let commandBuffer = context.commandQueue.makeCommandBuffer(), 36 | let blit = commandBuffer.makeBlitCommandEncoder() else { 37 | return 38 | } 39 | 40 | if dataBuffer == nil { 41 | dataBuffer = context.device.makeBuffer(length: texture.width * texture.height * MemoryLayout.size, options: MTLResourceOptions.storageModeShared) 42 | } 43 | 44 | blit.copy( 45 | from: texture, 46 | sourceSlice: 0, 47 | sourceLevel: 0, 48 | sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0), 49 | sourceSize: texture.size(), 50 | to: dataBuffer, 51 | destinationOffset: 0, 52 | destinationBytesPerRow: texture.width * MemoryLayout.size, 53 | destinationBytesPerImage: 0, 54 | options: [] 55 | ) 56 | 57 | blit.endEncoding() 58 | 59 | commandBuffer.addCompletedHandler { commandBuffer in 60 | self.updateBuffer() 61 | } 62 | 63 | commandBuffer.commit() 64 | } 65 | 66 | 67 | func updateBuffer() { 68 | 69 | guard let texture = texture else { return } 70 | 71 | let ptr: UnsafeMutablePointer = self.dataBuffer.contents().assumingMemoryBound(to: UInt8.self) 72 | let bptr = UnsafeBufferPointer(start: ptr, count: texture.width * texture.height * MemoryLayout.size) 73 | let data = [UInt8](bptr) 74 | 75 | newDataAvailable?(data) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/DepthBlend.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DepthBlend.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/9/17. 6 | // 7 | 8 | 9 | struct DepthBlendUniforms: Uniforms { 10 | var lowerThreshold: Float = 0.8 11 | } 12 | 13 | public 14 | class DepthBlend: Filter { 15 | 16 | var uniforms = DepthBlendUniforms() 17 | let depthRenderer = DepthRenderer() 18 | let resize = Resize() 19 | let blur = GaussianBlur() 20 | 21 | @objc var lowerThreshold: Float = 0.8 { 22 | didSet { 23 | clamp(&lowerThreshold, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | public init() { 29 | super.init(functionName: "depthBlend") 30 | blur.sigma = 0.1 31 | title = "Depth Blend" 32 | properties = [Property(key: "lowerThreshold", title: "Lower Threshold")] 33 | } 34 | 35 | public required init?(coder aDecoder: NSCoder) { 36 | fatalError("init(coder:) has not been implemented") 37 | } 38 | 39 | override func update() { 40 | super.update() 41 | 42 | uniforms.lowerThreshold = lowerThreshold 43 | updateUniforms(uniforms: uniforms) 44 | } 45 | 46 | public override func process() { 47 | blur.process() 48 | super.process() 49 | } 50 | 51 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 52 | super.configureCommandEncoder(commandEncoder) 53 | 54 | commandEncoder.setTexture(blur.texture, index: 2) 55 | commandEncoder.setTexture((source as? Camera)?.texture, index: 3) 56 | } 57 | 58 | override public var input: Input? { 59 | didSet { 60 | if let camera = source as? Camera { 61 | camera.mode = .depth 62 | camera.addTarget(depthRenderer) 63 | 64 | if let width = camera.texture?.width, let height = camera.texture?.height { 65 | resize.outputSize = MTLSize(width: width, height: height, depth: 1) 66 | } 67 | depthRenderer.addTarget(resize) 68 | resize.addTarget(blur) 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/DepthToGrayscale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DepthToGrayscale.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/8/17. 6 | // 7 | 8 | struct DepthToGrayscaleUniforms: Uniforms { 9 | var offset: Float = 0.5 10 | var range: Float = 0.5 11 | } 12 | 13 | public 14 | class DepthToGrayscale: Filter { 15 | 16 | var uniforms = DepthToGrayscaleUniforms() 17 | 18 | @objc public var offset: Float = 0.5 { 19 | didSet { 20 | // clamp(&offset, low: 0, high: 1) 21 | needsUpdate = true 22 | } 23 | } 24 | 25 | @objc public var range: Float = 0.5 { 26 | didSet { 27 | // clamp(&range, low: 0, high: 1) 28 | needsUpdate = true 29 | } 30 | } 31 | 32 | public init() { 33 | super.init(functionName: "depthToGrayscale") 34 | title = "Depth To Grayscale" 35 | properties = [ 36 | Property(key: "offset", title: "Offset"), 37 | Property(key: "range", title: "Range") 38 | ] 39 | } 40 | 41 | required public init?(coder aDecoder: NSCoder) { 42 | super.init(coder: aDecoder) 43 | } 44 | 45 | override func update() { 46 | if self.input == nil { return } 47 | 48 | uniforms.offset = offset 49 | uniforms.range = range 50 | updateUniforms(uniforms: uniforms) 51 | } 52 | 53 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 54 | super.configureCommandEncoder(commandEncoder) 55 | 56 | if let context = (source as? Camera)?.depthContext { 57 | if context.minDepth < offset { offset = context.minDepth } 58 | if context.maxDepth < range { range = context.maxDepth } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Dilate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dilate.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/17/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import MetalPerformanceShaders 11 | 12 | public 13 | class Dilate: MPS { 14 | 15 | var dilateValues: UnsafePointer! 16 | 17 | @objc var width: Float = 0.5 { 18 | didSet { 19 | clamp(&width, low: 0, high: 1) 20 | needsUpdate = true 21 | } 22 | } 23 | 24 | @objc var height: Float = 0.5 { 25 | didSet { 26 | clamp(&height, low: 0, high: 1) 27 | needsUpdate = true 28 | } 29 | } 30 | 31 | @objc var intensity: Float = 0.5 { 32 | didSet { 33 | clamp(&intensity, low: 0, high: 1) 34 | needsUpdate = true 35 | update() 36 | } 37 | } 38 | 39 | init() { 40 | super.init(functionName: nil) 41 | commonInit() 42 | } 43 | 44 | override init(functionName: String?) { 45 | super.init(functionName: nil) 46 | commonInit() 47 | } 48 | 49 | func commonInit() { 50 | let intense = intensity * 5.0 51 | // let values1 = [[-2.0 * intense, -intense, 0.0 ], 52 | // [-intense , 1.0 , intense ], 53 | // [0.0 , intense , 2.0 * intense]] 54 | 55 | let values = [-2.0 * intense, -intense, 0.0, -intense, 1.0 , intense, 0.0, intense , 2.0 * intense] 56 | updateDilateValues(values) 57 | 58 | title = "Dilate" 59 | properties = [Property(key: "intensity", title: "Intensity"), 60 | Property(key: "width", title: "Width"), 61 | Property(key: "height", title: "Height")] 62 | 63 | update() 64 | } 65 | 66 | func updateDilateValues(_ values: UnsafePointer!) { 67 | dilateValues = values 68 | } 69 | 70 | override func update() { 71 | let w: Int = (Int(width * 10) / 2) * 2 + 1 72 | let h: Int = (Int(height * 10) / 2) * 2 + 1 73 | 74 | kernel = MPSImageDilate(device: context.device, kernelWidth: w, kernelHeight: h, values: dilateValues) 75 | } 76 | 77 | required public init?(coder aDecoder: NSCoder) { 78 | super.init(coder: aDecoder) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Distortion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Distortion.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/22/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct DistortionUniforms: Uniforms { 12 | var centerX: Float = 0.5; 13 | var centerY: Float = 0.5; 14 | } 15 | 16 | public 17 | class Distortion: Filter { 18 | 19 | var uniforms = DistortionUniforms() 20 | 21 | @objc public var x: Float = 0.5 { 22 | didSet { 23 | clamp(&x, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var y: Float = 0.5 { 29 | didSet { 30 | clamp(&y, low: 0, high: 1) 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "distortion") 37 | title = "Distortion" 38 | properties = [Property(key: "x", title: "X"), 39 | Property(key: "y", title: "Y")] 40 | update() 41 | } 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | super.init(coder: aDecoder) 45 | } 46 | 47 | override func update() { 48 | if self.input == nil { return } 49 | uniforms.centerX = x 50 | uniforms.centerY = y 51 | updateUniforms(uniforms: uniforms) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Emboss.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Emboss.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | public 12 | class Emboss: Convolution { 13 | 14 | @objc public var intensity: Float = 0.0 { 15 | didSet { 16 | clamp(&intensity, low: 0, high: 1) 17 | needsUpdate = true 18 | } 19 | } 20 | 21 | override init() { 22 | super.init() 23 | title = "Emboss" 24 | properties = [Property(key: "intensity", title: "Intensity")] 25 | } 26 | 27 | required public init?(coder aDecoder: NSCoder) { 28 | super.init(coder: aDecoder) 29 | } 30 | 31 | override func update() { 32 | if self.input == nil { return } 33 | 34 | let intense = intensity * 1.25 35 | convolutionMatrix = [[-2.0 * intense, -intense, 0.0 ], 36 | [-intense , 1.0 , intense ], 37 | [0.0 , intense , 2.0 * intense]]; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Exposure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Exposure.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct ExposureUniforms: Uniforms { 12 | var exposure: Float = 0.5; 13 | } 14 | 15 | public 16 | class Exposure: Filter { 17 | 18 | var uniforms = ExposureUniforms() 19 | 20 | @objc public var exposure: Float = 0.5 { 21 | didSet { 22 | clamp(&exposure, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "exposure") 29 | title = "Exposure" 30 | properties = [Property(key: "exposure", title: "Exposure")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.exposure = Tools.convert(exposure, oldMin: 0.0, oldMid: 0.5, oldMax: 1.0, newMin: -1.5, newMid: 0.0, newMax: 2.0) 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/GaussianBlur.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GaussianBlur.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/17/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import MetalPerformanceShaders 11 | 12 | public 13 | class GaussianBlur: MPS { 14 | 15 | @objc public var sigma: Float = 0.5 { 16 | didSet { 17 | clamp(&sigma, low: 0, high: 1) 18 | kernel = MPSImageGaussianBlur(device: context.device, sigma: sigma * 80) 19 | (kernel as! MPSImageGaussianBlur).edgeMode = .clamp 20 | needsUpdate = true 21 | } 22 | } 23 | 24 | public init() { 25 | super.init(functionName: nil) 26 | commonInit() 27 | } 28 | 29 | override init(functionName: String?) { 30 | super.init(functionName: nil) 31 | commonInit() 32 | } 33 | 34 | func commonInit() { 35 | title = "Gaussian Blur" 36 | properties = [Property(key: "sigma", title: "Sigma")] 37 | sigma = 0.5 38 | } 39 | 40 | required public init?(coder aDecoder: NSCoder) { 41 | super.init(coder: aDecoder) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/HSV.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HSV.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct HSVUniforms: Uniforms { 12 | var hue: Float = 0.5 13 | var saturation: Float = 0.5 14 | var vibrancy: Float = 0.5 15 | } 16 | 17 | public 18 | class HSV: Filter { 19 | 20 | var uniforms = HSVUniforms() 21 | 22 | @objc public var hue: Float = 0.5 { 23 | didSet { 24 | clamp(&hue, low: 0, high: 1) 25 | needsUpdate = true 26 | } 27 | } 28 | 29 | @objc public var saturation: Float = 0.5 { 30 | didSet { 31 | clamp(&saturation, low: 0, high: 1) 32 | needsUpdate = true 33 | } 34 | } 35 | 36 | @objc public var vibrancy: Float = 0.5 { 37 | didSet { 38 | clamp(&vibrancy, low: 0, high: 1) 39 | needsUpdate = true 40 | } 41 | } 42 | 43 | public init() { 44 | super.init(functionName: "hsv") 45 | title = "HSV" 46 | properties = [Property(key: "hue" , title: "Hue"), 47 | Property(key: "saturation", title: "Saturation"), 48 | Property(key: "vibrancy" , title: "Vibrancy")] 49 | update() 50 | } 51 | 52 | required public init?(coder aDecoder: NSCoder) { 53 | super.init(coder: aDecoder) 54 | } 55 | 56 | override func update() { 57 | if self.input == nil { return } 58 | 59 | uniforms.hue = hue 60 | uniforms.saturation = saturation 61 | uniforms.vibrancy = vibrancy 62 | 63 | updateUniforms(uniforms: uniforms) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Haze.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Haze.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct HazeUniforms: Uniforms { 12 | var distance: Float = 0.5 13 | var slope: Float = 0.5; 14 | } 15 | 16 | public 17 | class Haze: Filter { 18 | 19 | var uniforms = HazeUniforms() 20 | 21 | @objc public var fade: Float = 0.0 { 22 | didSet { 23 | clamp(&fade, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | public init() { 29 | super.init(functionName: "haze") 30 | title = "Haze" 31 | properties = [Property(key: "fade", title: "Fade")] 32 | update() 33 | } 34 | 35 | required public init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | } 38 | 39 | override func update() { 40 | if self.input == nil { return } 41 | uniforms.distance = -fade 42 | // uniforms.slope = slope //distance * 0.6 - 0.3 43 | updateUniforms(uniforms: uniforms) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/HighlightShadow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighlightShadow.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct HighlightShadowUniforms: Uniforms { 12 | var highlights: Float = 1.0 13 | var shadows : Float = 0.0 14 | } 15 | 16 | public 17 | class HighlightShadow: Filter { 18 | 19 | var uniforms = HighlightShadowUniforms() 20 | 21 | @objc public var highlights: Float = 1.0 { 22 | didSet { 23 | clamp(&highlights, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var shadows: Float = 0.0 { 29 | didSet { 30 | clamp(&shadows, low: 0, high: 1) 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "highlightShadow") 37 | title = "Highlight/Shadow" 38 | properties = [Property(key: "highlights", title: "Highlights"), 39 | Property(key: "shadows" , title: "Shadows")] 40 | update() 41 | } 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | super.init(coder: aDecoder) 45 | } 46 | 47 | override func update() { 48 | if self.input == nil { return } 49 | uniforms.highlights = highlights 50 | uniforms.shadows = shadows 51 | updateUniforms(uniforms: uniforms) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/HoughLineDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTLHoughLineDetection.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // 7 | // 8 | 9 | public 10 | class HoughLineDetection: FilterGroup { 11 | 12 | let cannyEdgeDetection = CannyEdgeDetection() 13 | // let parallelLineTransform = 14 | let nonMaximumSuppressionFilter = NonMaximumSuppressionThreshod() 15 | 16 | override init() { 17 | super.init() 18 | 19 | title = "Hough Line Detection" 20 | 21 | add(cannyEdgeDetection) 22 | add(nonMaximumSuppressionFilter) 23 | } 24 | 25 | required public init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Hue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Hue.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct HueUniforms: Uniforms { 12 | var hue: Float = 0.0 13 | } 14 | 15 | public 16 | class Hue: Filter { 17 | 18 | var uniforms = HueUniforms() 19 | 20 | @objc public var hue: Float = 0.0 { 21 | didSet { 22 | clamp(&hue, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "hue") 29 | title = "Hue" 30 | properties = [Property(key: "hue", title: "Hue")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.hue = fmodf(hue * 360.0, 360.0) * (Float.pi / 180.0) 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Invert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Invert.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | public 12 | class Invert: Filter { 13 | 14 | public init() { 15 | super.init(functionName: "invert") 16 | title = "Invert" 17 | properties = [] 18 | } 19 | 20 | required public init?(coder aDecoder: NSCoder) { 21 | super.init(coder: aDecoder) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Kuwahara.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kuwahara.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct KuwaharaUniforms: Uniforms { 12 | var radius: Float = 0.5 13 | } 14 | 15 | public 16 | class Kuwahara: Filter { 17 | 18 | var uniforms = KuwaharaUniforms() 19 | 20 | @objc public var radius: Float = 0.5 { 21 | didSet { 22 | clamp(&radius, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "kuwahara") 29 | title = "Kuwahara" 30 | properties = [Property(key: "radius", title: "Radius")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.radius = round(Tools.convert(radius, oldMin: 0, oldMax: 1, newMin: 1, newMax: 10)) 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/LanczosScale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LanczosScale.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/17/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import MetalPerformanceShaders 11 | 12 | public 13 | class LanczosScale: MPS { 14 | 15 | var scaleTransform: MPSScaleTransform = MPSScaleTransform(scaleX: 1, scaleY: 1, translateX: 0, translateY: 0) 16 | var transformPointer: UnsafePointer! 17 | 18 | @objc public var scale: Float = 0.5 { 19 | didSet { 20 | clamp(&scale, low: 0, high: 1) 21 | needsUpdate = true 22 | } 23 | } 24 | 25 | init() { 26 | super.init(functionName: nil) 27 | commonInit() 28 | } 29 | 30 | override init(functionName: String?) { 31 | super.init(functionName: nil) 32 | commonInit() 33 | } 34 | 35 | func commonInit() { 36 | 37 | title = "Lanczos Scale" 38 | properties = [Property(key: "scale", title: "Scale")] //, 39 | // Property(key: "width", title: "Width"), 40 | // Property(key: "height", title: "Height")] 41 | 42 | transformPointer = withUnsafePointer(to: &scaleTransform, { (pointer: UnsafePointer) -> UnsafePointer! in 43 | return pointer 44 | }) 45 | 46 | kernel = MPSImageLanczosScale(device: context.device) 47 | 48 | update() 49 | } 50 | 51 | override func update() { 52 | (kernel as! MPSImageLanczosScale).scaleTransform = transformPointer 53 | } 54 | 55 | required public init?(coder aDecoder: NSCoder) { 56 | super.init(coder: aDecoder) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/LensFlare.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LensFlare.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 9/1/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct MTLLensFlareUniforms: Uniforms { 12 | var r: Float = 1.0 13 | var g: Float = 1.0 14 | var b: Float = 1.0 15 | 16 | var x: Float = 0.5 17 | var y: Float = 0.5 18 | 19 | var angleX: Float = 0.5 20 | var angleY: Float = 0.5 21 | 22 | var brightness: Float = 0.5; 23 | var showSun: Int = 1 24 | } 25 | 26 | public 27 | class LensFlare: Filter { 28 | 29 | var uniforms = MTLLensFlareUniforms() 30 | 31 | @objc public var color: UIColor = UIColor.white { 32 | didSet { 33 | needsUpdate = true 34 | } 35 | } 36 | 37 | @objc public var angle: CGPoint = CGPoint(x: 0.5, y: 0.5) { 38 | didSet { 39 | needsUpdate = true 40 | } 41 | } 42 | 43 | @objc public var center: CGPoint = CGPoint(x: 0.5, y: 0.5) { 44 | didSet { 45 | needsUpdate = true 46 | } 47 | } 48 | 49 | @objc public var brightness: Float = 0.5 { 50 | didSet { 51 | clamp(&brightness, low: 0, high: 1) 52 | needsUpdate = true 53 | } 54 | } 55 | 56 | @objc public var showSun: Bool = true { 57 | didSet { 58 | needsUpdate = true 59 | } 60 | } 61 | 62 | public init() { 63 | super.init(functionName: "lensFlare") 64 | title = "Lens Flare" 65 | properties = [Property(key: "color" , title: "Color" , propertyType: .color), 66 | Property(key: "center" , title: "Center" , propertyType: .point), 67 | Property(key: "angle" , title: "Angle" , propertyType: .point), 68 | Property(key: "showSun" , title: "Show Sun", propertyType: .bool ), 69 | Property(key: "brightness", title: "Brightness")] 70 | update() 71 | } 72 | 73 | required public init?(coder aDecoder: NSCoder) { 74 | super.init(coder: aDecoder) 75 | } 76 | 77 | override func update() { 78 | if self.input == nil { return } 79 | 80 | let components = color.cgColor.components 81 | if color == UIColor.white || color == UIColor.black { 82 | uniforms.r = Float((components?[0])!) 83 | uniforms.g = Float((components?[0])!) 84 | uniforms.b = Float((components?[0])!) 85 | } else { 86 | uniforms.r = Float((components?[0])!) 87 | uniforms.g = Float((components?[1])!) 88 | uniforms.b = Float((components?[2])!) 89 | } 90 | 91 | uniforms.x = Float(center.x) 92 | uniforms.y = Float(center.y) 93 | 94 | uniforms.angleX = Float(angle.x) 95 | uniforms.angleY = Float(angle.y) 96 | 97 | uniforms.brightness = brightness/10.0; 98 | uniforms.showSun = showSun ? 1 : 0 99 | 100 | updateUniforms(uniforms: uniforms) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Levels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Levels.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct LevelsUniforms: Uniforms { 12 | var min: Float = 0.0 13 | var mid: Float = 0.5 14 | var max: Float = 1.0 15 | var minOut: Float = 0.0 16 | var maxOut: Float = 1.0 17 | } 18 | 19 | public 20 | class Levels: Filter { 21 | var uniforms = LevelsUniforms() 22 | 23 | @objc public var min: Float = 0.0 { 24 | didSet { 25 | clamp(&min, low: 0, high: mid) 26 | needsUpdate = true 27 | } 28 | } 29 | 30 | @objc public var mid: Float = 0.5 { 31 | didSet { 32 | clamp(&mid, low: 0, high: 1) 33 | needsUpdate = true 34 | } 35 | } 36 | 37 | @objc public var max: Float = 1.0 { 38 | didSet { 39 | clamp(&max, low: 0, high: 1) 40 | needsUpdate = true 41 | } 42 | } 43 | 44 | @objc public var minOut: Float = 0.0 { 45 | didSet { 46 | clamp(&minOut, low: 0, high: 1) 47 | needsUpdate = true 48 | } 49 | } 50 | 51 | @objc public var maxOut: Float = 1.0 { 52 | didSet { 53 | clamp(&maxOut, low: 0, high: 1) 54 | needsUpdate = true 55 | } 56 | } 57 | 58 | public override func reset() { 59 | min = 0.0 60 | mid = 0.5 61 | max = 1.0 62 | minOut = 0.0 63 | maxOut = 1.0 64 | } 65 | 66 | public init() { 67 | super.init(functionName: "levels") 68 | title = "Levels" 69 | properties = [Property(key: "min" , title: "Minimum" ), 70 | Property(key: "mid" , title: "Middle" ), 71 | Property(key: "max" , title: "Maximum" ), 72 | Property(key: "minOut", title: "Minimum Output"), 73 | Property(key: "maxOut", title: "Maximum Output")] 74 | update() 75 | } 76 | 77 | required public init?(coder aDecoder: NSCoder) { 78 | super.init(coder: aDecoder) 79 | } 80 | 81 | override func update() { 82 | if self.input == nil { return } 83 | uniforms.min = min 84 | uniforms.mid = Tools.convert(mid, oldMin: 0.0, oldMid: 0.5, oldMax: 1.0, newMin: 0.0, newMid: 1.0, newMax: 10.0) 85 | uniforms.max = max 86 | uniforms.minOut = minOut 87 | uniforms.maxOut = maxOut 88 | updateUniforms(uniforms: uniforms) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/LineDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LineDetection.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/9/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct MTLLineDetectionUniforms { 12 | var sensitivity: Float = 0.5; 13 | } 14 | 15 | public 16 | class LineDetection: Filter { 17 | 18 | var uniforms = MTLLineDetectionUniforms() 19 | 20 | private var accumulatorBuffer: MTLBuffer! 21 | private var inputSize: CGSize? 22 | private let sobelEdgeDetectionThreshold = SobelEdgeDetectionThreshold() 23 | private let thetaCount: Int = 180 24 | lazy private var accumulator: [UInt8] = { 25 | return [UInt8](repeating: 0, count: Int(self.inputSize!.width) * self.thetaCount) 26 | }() 27 | 28 | @objc public var sensitivity: Float = 0.5 { 29 | didSet { 30 | clamp(&sensitivity, low: 0, high: 1) 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "lineDetection") 37 | title = "Line Detection" 38 | properties = [Property(key: "sensitivity", title: "Sensitivity")] 39 | 40 | sobelEdgeDetectionThreshold.addTarget(self) 41 | input = sobelEdgeDetectionThreshold 42 | 43 | update() 44 | } 45 | 46 | required public init?(coder aDecoder: NSCoder) { 47 | super.init(coder: aDecoder) 48 | } 49 | 50 | 51 | override func update() { 52 | if self.input == nil { return } 53 | 54 | if inputSize == nil { 55 | inputSize = context.processingSize 56 | } 57 | else { 58 | if accumulatorBuffer != nil { 59 | let length = Int(inputSize!.width) * thetaCount 60 | 61 | let data = Data(bytesNoCopy: accumulatorBuffer.contents(), count: length, deallocator: Data.Deallocator.none) 62 | data.copyBytes(to: &accumulator, count: data.count) 63 | 64 | // let m = accumulator.max() 65 | // print(m) 66 | } 67 | } 68 | 69 | uniforms.sensitivity = sensitivity 70 | uniformsBuffer = device.makeBuffer(bytes: &uniforms, length: MemoryLayout.size, options: .cpuCacheModeWriteCombined) 71 | } 72 | 73 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 74 | super.configureCommandEncoder(commandEncoder) 75 | 76 | let accumulator = [Float](repeating: 0, count: Int(inputSize!.width) * thetaCount) 77 | accumulatorBuffer = device.makeBuffer(bytes: accumulator, 78 | length: accumulator.count * MemoryLayout.size, 79 | options: .cpuCacheModeWriteCombined) 80 | commandEncoder.setBuffer(accumulatorBuffer, offset: 0, index: 1) 81 | } 82 | 83 | public override func process() { 84 | super.process() 85 | sobelEdgeDetectionThreshold.process() 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/LowPass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LowPass.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/13/17. 6 | // 7 | 8 | public 9 | class LowPass: FilterGroup { 10 | 11 | let buffer = Buffer() 12 | let blend = Blend() 13 | 14 | override init() { 15 | super.init() 16 | 17 | title = "Low Pass" 18 | blend.blendMode = BlendMode.dissolve.rawValue 19 | 20 | add(blend) 21 | 22 | blend --> buffer 23 | blend.add(input: buffer, at: 1) 24 | } 25 | 26 | required public init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/LuminanceThreshold.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LuminanceThreshold.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/24/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct LuminanceThresholdUniforms : Uniforms{ 12 | var threshold: Float = 0.5; 13 | } 14 | 15 | public 16 | class LuminanceThreshold: Filter { 17 | 18 | var uniforms = LuminanceThresholdUniforms() 19 | 20 | @objc public var threshold: Float = 0.5 { 21 | didSet { 22 | clamp(&threshold, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "luminanceThreshold") 29 | title = "Luminance Threshold" 30 | properties = [Property(key: "threshold", title: "Threshold")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.threshold = threshold 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Mosaic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mosaic.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/6/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct MosaicUniforms: Uniforms { 12 | var intensity: Float = 0.5; 13 | } 14 | 15 | public 16 | class Mosaic: Filter { 17 | 18 | var uniforms = MosaicUniforms() 19 | 20 | @objc public var intensity: Float = 0.5 { 21 | didSet { 22 | clamp(&intensity, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "mosaic") 29 | title = "Mosaic" 30 | properties = [Property(key: "intensity", title: "Intensity")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.intensity = intensity * 50 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/NonMaximumSuppression.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NonMaximumSuppression.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct NonMaximumSuppressionUniforms: Uniforms { 12 | var texelWidth: Float = 0.5; 13 | var texelHeight: Float = 0.5; 14 | var lowerThreshold: Float = 0.5; 15 | var upperThreshold: Float = 0.5; 16 | } 17 | 18 | public 19 | class NonMaximumSuppression: Filter { 20 | 21 | var uniforms = NonMaximumSuppressionUniforms() 22 | 23 | public var lowerThreshold: Float = 0.5 { 24 | didSet { 25 | clamp(&lowerThreshold, low: 0, high: 1) 26 | needsUpdate = true 27 | update() 28 | } 29 | } 30 | 31 | public var upperThreshold: Float = 0.5 { 32 | didSet { 33 | clamp(&upperThreshold, low: 0, high: 1) 34 | needsUpdate = true 35 | update() 36 | } 37 | } 38 | 39 | public init() { 40 | super.init(functionName: "nonMaximumSuppression") 41 | title = "Non Maximum Suppression" 42 | properties = [Property(key: "lowerThreshold", title: "Lower Threshold"), 43 | Property(key: "upperThreshold", title: "Upper Threshold")] 44 | update() 45 | } 46 | 47 | required public init?(coder aDecoder: NSCoder) { 48 | super.init(coder: aDecoder) 49 | } 50 | 51 | override func update() { 52 | if self.input == nil { return } 53 | 54 | uniforms.lowerThreshold = lowerThreshold 55 | uniforms.upperThreshold = upperThreshold 56 | uniforms.texelWidth = 1.0; 57 | uniforms.texelHeight = 1.0; 58 | 59 | updateUniforms(uniforms: uniforms) 60 | } 61 | 62 | } 63 | 64 | 65 | // Threshold 66 | 67 | struct NonMaximumSuppressionThreshodUniforms: Uniforms { 68 | var threshold: Float = 0.5; 69 | } 70 | 71 | public 72 | class NonMaximumSuppressionThreshod: Filter { 73 | 74 | var uniforms = NonMaximumSuppressionThreshodUniforms() 75 | 76 | @objc public var threshold: Float = 0.5 { 77 | didSet { 78 | clamp(&threshold, low: 0, high: 1) 79 | needsUpdate = true 80 | } 81 | } 82 | 83 | public init() { 84 | super.init(functionName: "nonMaximumSuppressionThreshold") 85 | title = "Non Maximum Suppression Threshold" 86 | properties = [Property(key: "threshold", title: "Threshold")] 87 | update() 88 | } 89 | 90 | required public init?(coder aDecoder: NSCoder) { 91 | super.init(coder: aDecoder) 92 | } 93 | 94 | override func update() { 95 | if self.input == nil { return } 96 | uniforms.threshold = threshold/5.0 + 0.01 97 | updateUniforms(uniforms: uniforms) 98 | } 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/PerlinNoise.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PerlinNoise.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/22/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct PerlinNoiseUniforms: Uniforms { 12 | var scale: Float = 0.5 13 | } 14 | 15 | public 16 | class PerlinNoise: Filter { 17 | 18 | var uniforms = PerlinNoiseUniforms() 19 | 20 | @objc public var scale: Float = 0.5 { 21 | didSet { 22 | clamp(&scale, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "perlinNoise") 29 | title = "Perlin Noise" 30 | properties = [Property(key: "scale", title: "Scale")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.scale = scale * 20 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Pixellate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pixellate.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct PixellateUniforms: Uniforms { 12 | var dotRadius: Float = 0.5; 13 | var aspectRatio: Float = 0.6667 14 | var fractionalWidthOfPixel: Float = 0.02 15 | } 16 | 17 | public 18 | class Pixellate: Filter { 19 | 20 | var uniforms = PixellateUniforms() 21 | var imageSize: CGSize? 22 | 23 | @objc public var dotRadius: Float = 0.5 { 24 | didSet { 25 | clamp(&dotRadius, low: 0, high: 1) 26 | needsUpdate = true 27 | } 28 | } 29 | 30 | public init() { 31 | super.init(functionName: "pixellate") 32 | title = "Pixellate" 33 | properties = [Property(key: "dotRadius", title: "Dot Radius")] 34 | update() 35 | } 36 | 37 | required public init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | } 40 | 41 | override func update() { 42 | if self.input == nil { return } 43 | 44 | // var add: Float = 0.2 45 | // if imageSize != nil { 46 | // add = Float(imageSize!.width) / 500.0 47 | // } 48 | 49 | uniforms.dotRadius = Tools.convert(dotRadius, oldMin: 0, oldMax: 1, newMin: 0.01, newMax: 3) 50 | updateUniforms(uniforms: uniforms) 51 | } 52 | 53 | override public var input: Input? { 54 | didSet { 55 | if originalImage != nil { 56 | imageSize = originalImage?.size 57 | uniforms.aspectRatio = 1.0 / Float(originalImage!.size.width / originalImage!.size.height) 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/PolkaDot.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.swift 3 | // Pods 4 | // 5 | // Created by Mohammad Fathi on 3/10/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct PolkaDotUniforms: Uniforms { 12 | var dotRadius: Float = 0.0 13 | } 14 | 15 | public 16 | class PolkaDot: Filter { 17 | 18 | var uniforms = PolkaDotUniforms() 19 | 20 | @objc public var dotRadius: Float = 0.0 { 21 | didSet { 22 | clamp(&dotRadius, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "polkaDot") 29 | title = "Polka Dot" 30 | properties = [Property(key: "dotRadius", title: "Dot Radius")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.dotRadius = Tools.convert(dotRadius, oldMin: 0, oldMax: 1, newMin: 0.05, newMax: 0.5) 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/RollingAverage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RollingAverage.swift 3 | // MTLImage-iOS10.0 4 | // 5 | // Created by Mohssen Fathi on 6/15/17. 6 | // 7 | 8 | public 9 | class RollingAverage: Filter { 10 | 11 | var textureQueue = [MTLTexture]() 12 | 13 | private var uniforms = RollingAverageUniforms() 14 | private struct RollingAverageUniforms: Uniforms { 15 | var bufferLength: Float = 10 16 | var currentBufferCount: Float = 0 17 | } 18 | 19 | public init() { 20 | super.init(functionName: "rollingAverage") 21 | title = "Rolling Average" 22 | properties = [] 23 | } 24 | 25 | required public init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | } 28 | 29 | override func update() { 30 | super.update() 31 | uniforms.currentBufferCount = Float(textureQueue.count) 32 | updateUniforms(uniforms: uniforms) 33 | } 34 | 35 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 36 | super.configureCommandEncoder(commandEncoder) 37 | 38 | guard let texture = texture else { return } 39 | 40 | textureQueue.append(texture.copy(device: device)) 41 | 42 | commandEncoder.setTexture(textureQueue.first, index: 2) 43 | commandEncoder.setTexture(textureQueue.last, index: 3) 44 | 45 | if textureQueue.count > bufferLength { 46 | textureQueue.remove(at: 0) 47 | } 48 | } 49 | 50 | // MARK: - Properties 51 | public var bufferLength: Int = 10 { 52 | didSet { 53 | uniforms.bufferLength = Float(bufferLength) 54 | needsUpdate = true 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Saturation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.swift 3 | // Pods 4 | // 5 | // Created by Mohammad Fathi on 3/10/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct SaturationUniforms: Uniforms { 12 | var saturation: Float = 0.5 13 | } 14 | 15 | public 16 | class Saturation: Filter { 17 | 18 | var uniforms = SaturationUniforms() 19 | 20 | @objc public var saturation: Float = 0.5 { 21 | didSet { 22 | clamp(&saturation, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "saturation") 29 | title = "Saturation" 30 | properties = [Property(key: "saturation", title: "Saturation")] 31 | 32 | update() 33 | } 34 | 35 | required public init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | } 38 | 39 | override func update() { 40 | if self.input == nil { return } 41 | uniforms.saturation = saturation * 2.0 42 | updateUniforms(uniforms: uniforms) 43 | } 44 | 45 | // override func configureArgumentTableWithCommandEncoder(commandEncoder: MTLComputeCommandEncoder?) { 46 | // var uniforms = AdjustSaturationUniforms(saturation: saturation) 47 | // 48 | // if uniformBuffer == nil { 49 | // uniformBuffer = context.device?.newBufferWithLength(sizeofValue(uniforms), options: .cpuCacheModeWriteCombined) 50 | // } 51 | // 52 | // memcpy(uniformBuffer.contents(), withBytes: &uniforms, sizeofValue(uniforms)) 53 | // commandEncoder?.setBuffer(uniformBuffer, offset: 0, atIndex: 0) 54 | // } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Sharpen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sharpen.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct SharpenUniforms: Uniforms { 12 | var sharpness: Float = 0.0; 13 | } 14 | 15 | public 16 | class Sharpen: Filter { 17 | var uniforms = SharpenUniforms() 18 | 19 | @objc public var sharpness: Float = 0.0 { 20 | didSet { 21 | clamp(&sharpness, low: 0, high: 1) 22 | needsUpdate = true 23 | } 24 | } 25 | 26 | public init() { 27 | super.init(functionName: "sharpen") 28 | title = "Sharpen" 29 | properties = [Property(key: "sharpness", title: "Sharpness")] 30 | update() 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | override func update() { 38 | if self.input == nil { return } 39 | uniforms.sharpness = sharpness * 4.0 40 | updateUniforms(uniforms: uniforms) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Sketch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sketch.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct SketchUniforms: Uniforms { 12 | var intensity: Float = 0.5; 13 | } 14 | 15 | public 16 | class Sketch: Filter { 17 | var uniforms = SketchUniforms() 18 | 19 | @objc public var intensity: Float = 0.5 { 20 | didSet { 21 | clamp(&intensity, low: 0, high: 1) 22 | needsUpdate = true 23 | } 24 | } 25 | 26 | public init() { 27 | super.init(functionName: "sketch") 28 | title = "Sketch" 29 | properties = [Property(key: "intensity", title: "Intensity")] 30 | update() 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | override func update() { 38 | if self.input == nil { return } 39 | 40 | let intense = intensity * 3.0 + 0.2 41 | // if context.processingSize != nil { 42 | // intense *= Float(2048.0/context.processingSize.width) 43 | // } 44 | uniforms.intensity = intense 45 | 46 | updateUniforms(uniforms: uniforms) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/SobelEdgeDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SobelEdgeDetection.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct SobelEdgeDetectionUniforms: Uniforms { 12 | var edgeStrength: Float = 0.5; 13 | } 14 | 15 | public 16 | class SobelEdgeDetection: Filter { 17 | 18 | var uniforms = SobelEdgeDetectionUniforms() 19 | 20 | @objc public var edgeStrength: Float = 0.5 { 21 | didSet { 22 | clamp(&edgeStrength, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "sobelEdgeDetection") 29 | title = "Sobel Edge Detection" 30 | properties = [Property(key: "edgeStrength", title: "Edge Strength")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.edgeStrength = edgeStrength * 3.0 + 0.2 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/SobelEdgeDetectionThreshold.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SobelEdgeDetectionThreshold.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/9/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct SobelEdgeDetectionThresholdUniforms: Uniforms { 12 | var threshold: Float = 0.5; 13 | } 14 | 15 | public 16 | class SobelEdgeDetectionThreshold: Filter { 17 | 18 | var uniforms = SobelEdgeDetectionThresholdUniforms() 19 | let sobelEdgeDetectionFilter = SobelEdgeDetection() 20 | 21 | @objc public var threshold: Float = 0.5 { 22 | didSet { 23 | clamp(&threshold, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var edgeStrength: Float = 0.0 { 29 | didSet { 30 | sobelEdgeDetectionFilter.edgeStrength = edgeStrength 31 | } 32 | } 33 | 34 | public init() { 35 | super.init(functionName: "sobelEdgeDetectionThreshold") 36 | title = "Sobel Edge Detection Threshold" 37 | properties = [Property(key: "threshold", title: "Threshold"), 38 | Property(key: "edgeStrength", title: "Edge Strength")] 39 | 40 | sobelEdgeDetectionFilter.addTarget(self) 41 | input = sobelEdgeDetectionFilter 42 | 43 | update() 44 | } 45 | 46 | required public init?(coder aDecoder: NSCoder) { 47 | super.init(coder: aDecoder) 48 | } 49 | 50 | override func update() { 51 | if self.input == nil { return } 52 | 53 | uniforms.threshold = threshold 54 | updateUniforms(uniforms: uniforms) 55 | } 56 | 57 | public override func process() { 58 | super.process() 59 | sobelEdgeDetectionFilter.process() 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Straighten.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Straighten.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 9/12/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct StraightenUniforms: Uniforms { 12 | var angle: Float = 0.5 13 | } 14 | 15 | public 16 | class Straighten: Filter { 17 | 18 | var uniforms = StraightenUniforms() 19 | 20 | @objc public var angle: Float = 0.5 { 21 | didSet { 22 | clamp(&angle, low: 0, high: 1) 23 | uniforms.angle = angle 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | public init() { 29 | super.init(functionName: "angle") 30 | title = "Straighten" 31 | properties = [Property(key: "angle", title: "Angle")] 32 | update() 33 | } 34 | 35 | required public init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | } 38 | 39 | override func update() { 40 | if self.input == nil { return } 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Tent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tent.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/18/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import MetalPerformanceShaders 11 | 12 | public 13 | class Tent: MPS { 14 | 15 | @objc public var radius: Float = 0.5 { 16 | didSet { 17 | clamp(&radius, low: 0, high: 1) 18 | kernel = MPSImageTent(device : context.device, 19 | kernelWidth : Tools.odd(Int(radius * 100.0)), 20 | kernelHeight: Tools.odd(Int(radius * 100.0))) 21 | (kernel as! MPSImageTent).edgeMode = .clamp 22 | needsUpdate = true 23 | } 24 | } 25 | 26 | init() { 27 | super.init(functionName: nil) 28 | commonInit() 29 | } 30 | 31 | override init(functionName: String?) { 32 | super.init(functionName: nil) 33 | commonInit() 34 | } 35 | 36 | func commonInit() { 37 | title = "Tent" 38 | properties = [Property(key: "radius" , title: "Radius")] 39 | radius = 0.5 40 | } 41 | 42 | required public init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Toon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Toon.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct ToonUniforms: Uniforms { 12 | var quantizationLevels: Float = 0.5; 13 | var threshold: Float = 0.0 14 | } 15 | 16 | public 17 | class Toon: Filter { 18 | var uniforms = ToonUniforms() 19 | 20 | @objc public var quantizationLevels: Float = 0.5 { 21 | didSet { 22 | clamp(&quantizationLevels, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | @objc public var threshold: Float = 0.5 { 28 | didSet { 29 | clamp(&threshold, low: 0, high: 1) 30 | needsUpdate = true 31 | } 32 | } 33 | 34 | public init() { 35 | super.init(functionName: "toon") 36 | title = "Toon" 37 | properties = [Property(key: "threshold" , title: "Threshold" ), 38 | Property(key: "quantizationLevels", title: "Quantization Levels")] 39 | update() 40 | } 41 | 42 | required public init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | } 45 | 46 | override func update() { 47 | if self.input == nil { return } 48 | uniforms.quantizationLevels = Tools.convert(quantizationLevels, oldMin: 0, oldMax: 1, newMin: 5, newMax: 15) 49 | uniforms.threshold = threshold * 0.8 + 0.2 50 | updateUniforms(uniforms: uniforms) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/UnsharpMask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnsharpMask.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 6/19/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct UnsharpMaskUniforms: Uniforms { 12 | var intensity: Float = 0.5; 13 | } 14 | 15 | public 16 | class UnsharpMask: Filter { 17 | 18 | var uniforms = UnsharpMaskUniforms() 19 | let blurFilter = GaussianBlur() 20 | 21 | @objc public var intensity: Float = 0.5 { 22 | didSet { 23 | clamp(&intensity, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var blurRadius: Float = 0.5 { 29 | didSet { 30 | blurFilter.sigma = blurRadius / 2.0 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "unsharpMask") 37 | 38 | title = "Unsharp Mask" 39 | properties = [Property(key: "blurRadius", title: "Blur Radius"), 40 | Property(key: "intensity" , title: "Intensity" ),] 41 | 42 | update() 43 | } 44 | 45 | required public init?(coder aDecoder: NSCoder) { 46 | super.init(coder: aDecoder) 47 | } 48 | 49 | override func update() { 50 | 51 | if self.input == nil { return } 52 | 53 | uniforms.intensity = Tools.convert(intensity, oldMin: 0, oldMid: 0.5, oldMax: 1, newMin: 0.5, newMid: 1.0, newMax: 2.3) 54 | updateUniforms(uniforms: uniforms) 55 | } 56 | 57 | override func configureCommandEncoder(_ commandEncoder: MTLComputeCommandEncoder) { 58 | super.configureCommandEncoder(commandEncoder) 59 | 60 | commandEncoder.setTexture(blurFilter.texture, index: 2) 61 | } 62 | 63 | public override func processIfNeeded() { 64 | 65 | blurFilter.processIfNeeded() 66 | 67 | super.processIfNeeded() 68 | } 69 | 70 | public override var input: Input? { 71 | didSet { 72 | if input == nil { 73 | blurFilter.input?.removeTarget(blurFilter) 74 | } else { 75 | input! --> blurFilter 76 | } 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Vignette.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vignette.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct VignetteUniforms: Uniforms { 12 | var x: Float = 0.0 13 | var y: Float = 0.0 14 | 15 | var r: Float = 1.0 16 | var g: Float = 1.0 17 | var b: Float = 1.0 18 | 19 | var start: Float = 0.25 20 | var end: Float = 0.7 21 | } 22 | 23 | public 24 | class Vignette: Filter { 25 | 26 | var uniforms = VignetteUniforms() 27 | 28 | @objc public var center: CGPoint = CGPoint(x: 0.5, y: 0.5) { 29 | didSet { 30 | needsUpdate = true 31 | } 32 | } 33 | 34 | @objc public var color: UIColor = UIColor.black { 35 | didSet { 36 | needsUpdate = true 37 | } 38 | } 39 | 40 | @objc public var start: Float = 0.25 { 41 | didSet { 42 | clamp(&start, low: 0, high: 1) 43 | needsUpdate = true 44 | } 45 | } 46 | 47 | @objc public var end: Float = 0.7 { 48 | didSet { 49 | clamp(&end, low: 0, high: 1) 50 | needsUpdate = true 51 | update() 52 | } 53 | } 54 | 55 | public override func reset() { 56 | center = CGPoint(x: 0.5, y: 0.5) 57 | color = UIColor.black 58 | start = 0.25 59 | end = 0.7 60 | } 61 | 62 | public init() { 63 | super.init(functionName: "vignette") 64 | title = "Vignette" 65 | properties = [Property(key: "center", title: "Center", propertyType: .point), 66 | Property(key: "color" , title: "Color" , propertyType: .color), 67 | Property(key: "start" , title: "Start" ), 68 | Property(key: "end" , title: "End" )] 69 | update() 70 | } 71 | 72 | required public init?(coder aDecoder: NSCoder) { 73 | super.init(coder: aDecoder) 74 | } 75 | 76 | override public var needsUpdate: Bool { 77 | didSet { 78 | if needsUpdate == true { update() } 79 | } 80 | } 81 | 82 | override func update() { 83 | if self.input == nil { return } 84 | 85 | let components = color.cgColor.components 86 | if color == UIColor.white || color == UIColor.black { 87 | uniforms.r = Float((components?[0])!) 88 | uniforms.g = Float((components?[0])!) 89 | uniforms.b = Float((components?[0])!) 90 | } else { 91 | uniforms.r = Float((components?[0])!) 92 | uniforms.g = Float((components?[1])!) 93 | uniforms.b = Float((components?[2])!) 94 | } 95 | 96 | uniforms.x = Float(center.x) 97 | uniforms.y = Float(center.y) 98 | uniforms.start = start 99 | uniforms.end = end 100 | 101 | updateUniforms(uniforms: uniforms) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Voronoi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Voronai.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 9/9/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct VoronoiUniforms: Uniforms { 12 | var time: Float = 0.0 13 | var size: Float = 0.5 14 | var animate: Float = 0.0 15 | } 16 | 17 | public 18 | class Voronoi: Filter { 19 | 20 | var uniforms = VoronoiUniforms() 21 | 22 | @objc public var size: Float = 0.5 { 23 | didSet { 24 | clamp(&size, low: 0.0, high: 1.0) 25 | } 26 | } 27 | 28 | @objc public var animate: Bool = false 29 | 30 | public init() { 31 | super.init(functionName: "voronoi") 32 | title = "Voronoi" 33 | properties = [Property(key: "animate", title: "Animate", propertyType: .bool), 34 | Property(key: "size" , title: "Density")] 35 | update() 36 | } 37 | 38 | public override func process() { 39 | update() 40 | super.process() 41 | needsUpdate = true 42 | } 43 | 44 | required public init?(coder aDecoder: NSCoder) { 45 | super.init(coder: aDecoder) 46 | } 47 | 48 | override func update() { 49 | if self.input == nil { return } 50 | 51 | uniforms.time += 1.0/60.0 52 | uniforms.size = size 53 | uniforms.animate = animate ? 1.0 : 0.0 54 | 55 | updateUniforms(uniforms: uniforms) 56 | } 57 | 58 | public override var continuousUpdate: Bool { 59 | return true 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/Water.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Water.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/9/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct WaterUniforms: Uniforms { 12 | var time: Float = 0.0 13 | var speed: Float = 0.5 14 | var frequency: Float = 0.5 15 | var intensity: Float = 0.5 16 | var emboss: Float = 0.5 17 | var delta: Float = 0.5 18 | var intence: Float = 0.5 19 | } 20 | 21 | public 22 | class Water: Filter { 23 | var uniforms = WaterUniforms() 24 | 25 | @objc public var speed: Float = 0.5 { 26 | didSet { 27 | clamp(&speed, low: 0, high: 1) 28 | } 29 | } 30 | 31 | @objc public var frequency: Float = 0.5 { 32 | didSet { 33 | clamp(&frequency, low: 0, high: 1) 34 | } 35 | } 36 | 37 | @objc public var intensity: Float = 0.5 { 38 | didSet { 39 | clamp(&intensity, low: 0, high: 1) 40 | } 41 | } 42 | 43 | @objc public var emboss: Float = 0.5 { 44 | didSet { 45 | clamp(&emboss, low: 0, high: 1) 46 | } 47 | } 48 | 49 | @objc public var delta: Float = 0.5 { 50 | didSet { 51 | clamp(&delta, low: 0, high: 1) 52 | } 53 | } 54 | 55 | @objc public var intence: Float = 0.5 { 56 | didSet { clamp(&intence, low: 0, high: 1) } 57 | } 58 | 59 | public init() { 60 | super.init(functionName: "water") 61 | title = "Water" 62 | properties = [Property(key: "speed", title: "Speed" ), 63 | Property(key: "frequency", title: "Frequency"), 64 | Property(key: "intensity", title: "Intensity"), 65 | Property(key: "emboss", title: "Enboss" ), 66 | Property(key: "delta", title: "Delta" ), 67 | Property(key: "intence", title: "Intence" )] 68 | update() 69 | } 70 | 71 | required public init?(coder aDecoder: NSCoder) { 72 | super.init(coder: aDecoder) 73 | } 74 | 75 | public override func process() { 76 | update() 77 | super.process() 78 | needsUpdate = true 79 | } 80 | 81 | override func update() { 82 | if self.input == nil { return } 83 | 84 | uniforms.speed = speed * 0.5 + 0.1 85 | uniforms.intensity = Tools.convert(intensity, oldMin: 0, oldMax: 1, newMin: 2.0, newMax: 6.0) 86 | uniforms.emboss = emboss + 0.5 87 | uniforms.frequency = frequency * 6.0 + 3.0 88 | uniforms.delta = delta * 60.0 + 30.0 89 | uniforms.intence = intence * 500.0 + 400.0 90 | uniforms.time += 1.0/60.0 91 | 92 | updateUniforms(uniforms: uniforms) 93 | } 94 | 95 | override public var continuousUpdate: Bool { 96 | return true 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/WeakPixelInclusion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeakPixelInclusion.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct WeakPixelInclusionUniforms: Uniforms { 12 | 13 | } 14 | 15 | public 16 | class WeakPixelInclusion: Filter { 17 | 18 | var uniforms = WeakPixelInclusionUniforms() 19 | 20 | public init() { 21 | super.init(functionName: "weakPixelInclusion") 22 | title = "Weak Pixel Inclusion" 23 | properties = [] 24 | update() 25 | } 26 | 27 | required public init?(coder aDecoder: NSCoder) { 28 | super.init(coder: aDecoder) 29 | } 30 | 31 | override func update() { 32 | if self.input == nil { return } 33 | updateUniforms(uniforms: uniforms) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/WhiteBalance.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WhiteBalance.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct WhiteBalanceUniforms: Uniforms { 12 | var temperature: Float = 0.5; 13 | var tint: Float = 0.5; 14 | } 15 | 16 | public 17 | class WhiteBalance: Filter { 18 | 19 | var uniforms = WhiteBalanceUniforms() 20 | 21 | @objc public var temperature: Float = 0.5 { 22 | didSet { 23 | clamp(&temperature, low: 0, high: 1) 24 | needsUpdate = true 25 | } 26 | } 27 | 28 | @objc public var tint: Float = 0.5 { 29 | didSet { 30 | clamp(&tint, low: 0, high: 1) 31 | needsUpdate = true 32 | } 33 | } 34 | 35 | public init() { 36 | super.init(functionName: "whiteBalance") 37 | title = "White Balance" 38 | properties = [Property(key: "temperature", title: "Temperature"), 39 | Property(key: "tint" , title: "Tint" )] 40 | update() 41 | } 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | super.init(coder: aDecoder) 45 | } 46 | 47 | override func update() { 48 | if self.input == nil { return } 49 | uniforms.temperature = temperature * 2.0 - 1.0 50 | uniforms.tint = tint * 4.0 - 2.0 51 | updateUniforms(uniforms: uniforms) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /MTLImage/Sources/Filters/XYDerivative.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XYDerivative.swift 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | struct XYDerivativeUniforms: Uniforms { 12 | var edgeStrength: Float = 0.5 13 | } 14 | 15 | public 16 | class XYDerivative: Filter { 17 | 18 | var uniforms = XYDerivativeUniforms() 19 | 20 | @objc var edgeStrength: Float = 0.5 { 21 | didSet { 22 | clamp(&edgeStrength, low: 0, high: 1) 23 | needsUpdate = true 24 | } 25 | } 26 | 27 | public init() { 28 | super.init(functionName: "xyDerivative") 29 | title = "XY Derivative" 30 | properties = [Property(key: "edgeStrength", title: "Edge Strength")] 31 | update() 32 | } 33 | 34 | required public init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | override func update() { 39 | if self.input == nil { return } 40 | uniforms.edgeStrength = edgeStrength * 3.0 + 0.2 41 | updateUniforms(uniforms: uniforms) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Brightness.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Brightness.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct BrightnessUniforms { 13 | float brightness; 14 | }; 15 | 16 | kernel void brightness(texture2d inTexture [[texture(0)]], 17 | texture2d outTexture [[texture(1)]], 18 | constant BrightnessUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | outTexture.write(float4((color.rgb + uniforms.brightness), color.a), gid); 23 | } 24 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Camera.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Camera.metal 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 4/12/16. 6 | // 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | kernel void camera(texture2d inTexture [[texture(0)]], 13 | texture2d outTexture [[texture(1)]], 14 | uint2 gid [[thread_position_in_grid]]) 15 | { 16 | float4 color = inTexture.read(gid); 17 | outTexture.write(float4(color.rgb, 1.0), gid); 18 | } 19 | 20 | //kernel void camera(texture2d yTexture [[texture(0)]], 21 | // texture2d cbcrTexture [[texture(1)]], 22 | // texture2d outTexture [[texture(2)]], 23 | // uint2 gid [[thread_position_in_grid]]) 24 | //{ 25 | // float3 colorOffset = float3(-(16.0/255.0), -0.5, -0.5); 26 | // float3x3 colorMatrix = float3x3(float3(1.164, 1.164, 1.164), 27 | // float3(0.000, -0.392, 2.017), 28 | // float3(1.596, -0.813, 0.000)); 29 | //// float3x3 colorMatrix = float3x3(float3(1.000, 1.000, 1.000), 30 | //// float3(0.000, -0.187, 1.856), 31 | //// float3(1.575, -0.468, 0.000)); 32 | // 33 | // float y = yTexture.read(gid).r; 34 | // float2 cbcr = cbcrTexture.read(uint2(gid.x/2, gid.y/2)).rg; 35 | // float3 ycbcr = float3(y, cbcr); 36 | // float3 rbg = colorMatrix * (ycbcr + colorOffset); 37 | // float3 rgb = float3(rbg.b, rbg.g, rbg.r); 38 | // 39 | // outTexture.write(float4(float3(rgb), 1.0), gid); 40 | //} -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/ColorMask.metal: -------------------------------------------------------------------------------- 1 | // 2 | // ColorMask.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/29/17. 6 | // 7 | 8 | #include 9 | using namespace metal; 10 | 11 | struct ColorMaskUniforms { 12 | float r; 13 | float g; 14 | float b; 15 | float a; 16 | float threshold; 17 | }; 18 | 19 | float3 normalizeColor(float3 color); 20 | 21 | kernel void colorMask(texture2d inTexture [[ texture(0) ]], 22 | texture2d outTexture [[ texture(1) ]], 23 | constant ColorMaskUniforms &uniforms [[ buffer(0) ]], 24 | uint2 gid [[thread_position_in_grid]]) 25 | { 26 | float4 color = inTexture.read(gid); 27 | float4 match = float4(uniforms.r, uniforms.g, uniforms.b, uniforms.a); 28 | 29 | // No color set 30 | if (uniforms.a < 1.0) { 31 | outTexture.write(color, gid); 32 | return; 33 | } 34 | 35 | float d = distance(normalizeColor(color.rgb), normalizeColor(match.rgb)); 36 | if (d > uniforms.threshold) { 37 | outTexture.write(float4(1, 1, 1, 0), gid); 38 | return; 39 | } 40 | 41 | outTexture.write(color, gid); 42 | } 43 | 44 | float3 normalizeColor(float3 color) { 45 | return color / max(dot(color, float3(1.0/3.0)), 0.3); 46 | } 47 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Contrast.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Contrast.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct ContrastUniforms { 13 | float contrast; 14 | }; 15 | 16 | kernel void contrast(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant ContrastUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | outTexture.write(float4(((color.rgb - 0.5) * uniforms.contrast + 0.5), color.a), gid); 23 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Convolution.metal: -------------------------------------------------------------------------------- 1 | // 2 | // 3x3Convolution.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | kernel void convolution(texture2d inTexture [[ texture(0) ]], 13 | texture2d outTexture [[ texture(1) ]], 14 | texture2d convolutionMatrix [[ texture(2) ]], 15 | uint2 gid [[thread_position_in_grid]]) 16 | { 17 | float4 centerColor = inTexture.read(gid ); 18 | float3 bottomColor = inTexture.read(uint2(gid.x , gid.y - 1)).rgb; 19 | float3 bottomLeftColor = inTexture.read(uint2(gid.x - 1, gid.y - 1)).rgb; 20 | float3 bottomRightColor = inTexture.read(uint2(gid.x + 1, gid.y - 1)).rgb; 21 | float3 leftColor = inTexture.read(uint2(gid.x - 1, gid.y )).rgb; 22 | float3 rightColor = inTexture.read(uint2(gid.x + 1, gid.y )).rgb; 23 | float3 topColor = inTexture.read(uint2(gid.x , gid.y + 1)).rgb; 24 | float3 topRightColor = inTexture.read(uint2(gid.x + 1, gid.y + 1)).rgb; 25 | float3 topLeftColor = inTexture.read(uint2(gid.x - 1, gid.y + 1)).rgb; 26 | 27 | float3 resultColor = topLeftColor * convolutionMatrix.read(uint2(0, 0)).rrr + 28 | topColor * convolutionMatrix.read(uint2(0, 1)).rrr + 29 | topRightColor * convolutionMatrix.read(uint2(0, 2)).rrr; 30 | 31 | resultColor += leftColor * convolutionMatrix.read(uint2(1, 0)).rrr + 32 | centerColor.rgb * convolutionMatrix.read(uint2(1, 1)).rrr + 33 | rightColor * convolutionMatrix.read(uint2(1, 2)).rrr; 34 | 35 | resultColor += bottomLeftColor * convolutionMatrix.read(uint2(2, 0)).rrr + 36 | bottomColor * convolutionMatrix.read(uint2(2, 1)).rrr + 37 | bottomRightColor * convolutionMatrix.read(uint2(2, 2)).rrr; 38 | 39 | outTexture.write(float4(resultColor, centerColor.a), gid); 40 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Crop.metal: -------------------------------------------------------------------------------- 1 | // 2 | // crop.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/14/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | float convertValue(float value, float oldMin, float oldMax, float newMin, float newMax); 13 | 14 | struct CropUniforms { 15 | float x; 16 | float y; 17 | float width; 18 | float height; 19 | int fit; 20 | }; 21 | 22 | kernel void crop(texture2d inTexture [[ texture(0)]], 23 | texture2d outTexture [[ texture(1)]], 24 | constant CropUniforms &uniforms [[ buffer(0) ]], 25 | uint2 gid [[thread_position_in_grid]]) 26 | { 27 | float4 color = inTexture.read(gid); 28 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 29 | float ratio = size.x / size.y; 30 | float2 uv = float2(gid)/size; 31 | 32 | if (uniforms.fit) { 33 | 34 | float newRatio = (uniforms.width * size.x) / (uniforms.height * size.y); 35 | float4 newFrame; 36 | 37 | // [ x, y, z, w ] 38 | if (newRatio > ratio) { // fit width 39 | newFrame.x = 0; 40 | newFrame.z = size.x; 41 | newFrame.w = size.x / newRatio; 42 | newFrame.y = (size.y - newFrame.w) / 2.0; 43 | } 44 | else { // fit height 45 | newFrame.y = 0; 46 | newFrame.w = size.y; 47 | newFrame.z = size.y * newRatio; 48 | newFrame.x = (size.x - newFrame.z) / 2.0; 49 | } 50 | 51 | if (gid.x < newFrame.x || gid.y < newFrame.y || gid.x > newFrame.x + newFrame.z || gid.y > newFrame.y + newFrame.w) { 52 | color = float4(0); 53 | } 54 | else { 55 | float vx = convertValue(uv.x, newFrame.x/size.x, (newFrame.x + newFrame.z)/size.x, 0, 1); 56 | float vy = convertValue(uv.y, newFrame.y/size.y, (newFrame.y + newFrame.w)/size.y, 0, 1); 57 | float x = (uniforms.x + vx * uniforms.width ) * size.x; 58 | float y = (uniforms.y + vy * uniforms.height) * size.y; 59 | 60 | color = inTexture.read(uint2(x, y)); 61 | } 62 | } 63 | else { 64 | if (uv.x < uniforms.x || uv.y < uniforms.y || uv.x > uniforms.x + uniforms.width || uv.y > uniforms.y + uniforms.height) { 65 | color = float4(0); 66 | } 67 | } 68 | 69 | outTexture.write(color, gid); 70 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/CrossHatch.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/10/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct CrossHatchUniforms { 13 | float crossHatchSpacing; 14 | float lineWidth; 15 | }; 16 | 17 | kernel void crossHatch(texture2d inTexture [[ texture(0) ]], 18 | texture2d outTexture [[ texture(1) ]], 19 | constant CrossHatchUniforms &uniforms [[ buffer(0) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | float4 color = inTexture.read(gid); 23 | float luminance = dot(color.rgb, float3(0.2125, 0.7154, 0.0721)); 24 | 25 | float crossHatchSpacing = uniforms.crossHatchSpacing; 26 | float lineWidth = uniforms.lineWidth; 27 | 28 | float4 outColor = float4(1.0, 1.0, 1.0, 1.0); 29 | 30 | if (luminance < 1.00) { 31 | if (fmod(gid.x + gid.y, crossHatchSpacing) <= lineWidth) { 32 | outColor = float4(0.0, 0.0, 0.0, 1.0); 33 | } 34 | } 35 | if (luminance < 0.75) { 36 | if (fmod(gid.x - gid.y, crossHatchSpacing) <= lineWidth) { 37 | outColor = float4(0.0, 0.0, 0.0, 1.0); 38 | } 39 | } 40 | if (luminance < 0.50) { 41 | if (fmod(gid.x + gid.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth) { 42 | outColor = float4(0.0, 0.0, 0.0, 1.0); 43 | } 44 | } 45 | if (luminance < 0.3) { 46 | if (fmod(gid.x - gid.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth) { 47 | outColor = float4(0.0, 0.0, 0.0, 1.0); 48 | } 49 | } 50 | 51 | // float2 size = float2(inTexture.get_width(), inTexture.get_height()); 52 | // float x = gid.x/size.x; 53 | // float y = gid.y/size.y; 54 | 55 | // if (luminance < 1.00) { 56 | // if (fmod(x + y, crossHatchSpacing) <= lineWidth) { 57 | // outColor = float4(0.0, 0.0, 0.0, 1.0); 58 | // } 59 | // } 60 | // if (luminance < 0.75) { 61 | // if (fmod(x - y, crossHatchSpacing) <= lineWidth) { 62 | // outColor = float4(0.0, 0.0, 0.0, 1.0); 63 | // } 64 | // } 65 | // if (luminance < 0.50) { 66 | // if (fmod(x + y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth) { 67 | // outColor = float4(0.0, 0.0, 0.0, 1.0); 68 | // } 69 | // } 70 | // if (luminance < 0.3) { 71 | // if (fmod(x - y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth) { 72 | // outColor = float4(0.0, 0.0, 0.0, 1.0); 73 | // } 74 | // } 75 | 76 | outTexture.write(outColor, gid); 77 | } 78 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/DefaultShaders.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Shaders.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/25/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | typedef struct { 13 | float4 renderedCoordinate [[position]]; 14 | float2 textureCoordinate; 15 | } TextureMappingVertex; 16 | 17 | vertex TextureMappingVertex vertex_main(unsigned int vertex_id [[ vertex_id ]]) { 18 | float4x4 renderedCoordinates = float4x4(float4( -1.0, -1.0, 0.0, 1.0 ), /// (x, y, depth, W) 19 | float4( 1.0, -1.0, 0.0, 1.0 ), 20 | float4( -1.0, 1.0, 0.0, 1.0 ), 21 | float4( 1.0, 1.0, 0.0, 1.0 )); 22 | 23 | float4x2 textureCoordinates = float4x2(float2( 0.0, 1.0 ), /// (x, y) 24 | float2( 1.0, 1.0 ), 25 | float2( 0.0, 0.0 ), 26 | float2( 1.0, 0.0 )); 27 | TextureMappingVertex outVertex; 28 | outVertex.renderedCoordinate = renderedCoordinates[vertex_id]; 29 | outVertex.textureCoordinate = textureCoordinates[vertex_id]; 30 | 31 | return outVertex; 32 | } 33 | 34 | fragment half4 fragment_main(TextureMappingVertex mappingVertex [[ stage_in ]], 35 | texture2d texture [[ texture(0) ]]) { 36 | constexpr sampler s(address::clamp_to_edge, filter::linear); 37 | 38 | return half4(texture.sample(s, mappingVertex.textureCoordinate)); 39 | } 40 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/DepthBlend.metal: -------------------------------------------------------------------------------- 1 | // 2 | // DepthBlend.metal 3 | // MTLImage_Example 4 | // 5 | // Created by Mohssen Fathi on 6/9/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct DepthBlendUniforms { 13 | float lowerThreshold; 14 | }; 15 | 16 | // Compute kernel 17 | kernel void depthBlend(texture2d inputTexture [[ texture(0) ]], 18 | texture2d outputTexture [[ texture(1) ]], 19 | texture2d depthTexture [[ texture(2) ]], 20 | texture2d sourceTexture [[ texture(3) ]], 21 | constant DepthBlendUniforms& uniforms [[ buffer(0) ]], 22 | uint2 gid [[ thread_position_in_grid ]]) 23 | { 24 | 25 | float2 depthSize = float2(depthTexture.get_width(), depthTexture.get_height()); 26 | float2 texSize = float2(inputTexture.get_width(), inputTexture.get_height()); 27 | float2 normalizedCoord = float2(gid.x/texSize.x, gid.y/texSize.y); 28 | uint2 depthGid = uint2(normalizedCoord.x * depthSize.x, normalizedCoord.y * depthSize.y); 29 | 30 | float depth = depthTexture.read(depthGid).x; 31 | float4 texColor = inputTexture.read(gid); 32 | float4 sourceColor = sourceTexture.read(gid); 33 | 34 | if (depth < uniforms.lowerThreshold) { 35 | outputTexture.write(sourceColor, gid); 36 | return; 37 | } 38 | 39 | float4 color = float4(mix(sourceColor.rgb, texColor.rgb, depth), 1.0); 40 | outputTexture.write(color, gid); 41 | } 42 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/DepthMask.metal: -------------------------------------------------------------------------------- 1 | // 2 | // DepthMask.metal 3 | // MTLImage_Example 4 | // 5 | // Created by Mohssen Fathi on 6/6/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct DepthMaskUniforms { 13 | float edgeStrength; 14 | }; 15 | 16 | kernel void depthMask(texture2d inTexture [[texture(0)]], 17 | texture2d outTexture [[texture(1)]], 18 | texture2d depthMap [[texture(2)]], 19 | constant DepthMaskUniforms &uniforms [[buffer(0)]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | outTexture.write(float4(0.0, 0.0, 1.0, 1.0), gid); 23 | } 24 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/DepthRenderer.metal: -------------------------------------------------------------------------------- 1 | // 2 | // DepthToGrayscale.metal 3 | // MTLImage_Example 4 | // 5 | // Created by Mohssen Fathi on 6/8/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct DepthRendererUniforms { 13 | float offset; 14 | float range; 15 | }; 16 | 17 | // Compute kernel 18 | kernel void depthRenderer(texture2d inputTexture [[ texture(0) ]], 19 | texture2d outputTexture [[ texture(1) ]], 20 | constant DepthRendererUniforms& uniforms [[ buffer(0) ]], 21 | uint2 gid [[ thread_position_in_grid ]]) 22 | { 23 | 24 | // float2 inputSize = float2(inputTexture.get_width(), inputTexture.get_height()); 25 | // float2 outputSize = float2(outputTexture.get_width(), outputTexture.get_height()); 26 | // float2 normalizedCoord = float2(float(gid.x)/inputSize.x, float(gid.y)/inputSize.y); 27 | // uint2 outputGid = uint2(normalizedCoord.x * outputSize.x, normalizedCoord.y * outputSize.y); 28 | 29 | float depth = inputTexture.read(gid).x; 30 | 31 | // Normalize the value between 0 and 1 32 | // depth = (depth - uniforms.offset) / (uniforms.range); 33 | 34 | outputTexture.write(float4(float3(depth), 1.0), gid); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Distortion.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Distortion.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/22/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct DistortionUniforms { 13 | float centerX; 14 | float centerY; 15 | }; 16 | 17 | kernel void distortion(texture2d inTexture [[texture(0)]], 18 | texture2d outTexture [[texture(1)]], 19 | constant DistortionUniforms &uniforms [[ buffer(0) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | float4 color = inTexture.read(gid); 23 | float2 textureSize = float2(inTexture.get_width(), inTexture.get_height()); 24 | float2 texCoord = float2(gid) / textureSize; 25 | 26 | float2 center = float2(uniforms.centerX, uniforms.centerY); 27 | float2 normCoord = texCoord; 28 | float2 normCenter = center; 29 | 30 | normCoord -= normCenter; 31 | float2 s = sign(normCoord); 32 | normCoord = abs(normCoord); 33 | normCoord = 0.5 * normCoord + 0.5 * smoothstep(0.25, 0.5, normCoord) * normCoord; 34 | normCoord = s * normCoord; 35 | 36 | normCoord += normCenter; 37 | normCoord = float2(normCoord.x * textureSize.x, normCoord.y * textureSize.y); 38 | 39 | uint2 textureCoordinateToUse = uint2(normCoord + 0.5); 40 | 41 | outTexture.write(color, textureCoordinateToUse); 42 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Exposure.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Exposure.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct ExposureUniforms { 13 | float exposure; 14 | }; 15 | 16 | kernel void exposure(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant ExposureUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | outTexture.write(float4(color.rgb * pow(2.0, uniforms.exposure), color.a), gid); 23 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/GaussianBlur.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/10/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct GaussianBlurUniforms { 13 | float blurRadius; 14 | float sigma; 15 | }; 16 | 17 | kernel void gaussianBlur(texture2d inTexture [[texture(0)]], 18 | texture2d outTexture [[texture(1)]], 19 | texture2d weights [[texture(2)]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | int size = weights.get_width(); 23 | int radius = size / 2; 24 | 25 | float4 accumColor(0, 0, 0, 0); 26 | for (int j = 0; j < size; ++j) { 27 | for (int i = 0; i < size; ++i) { 28 | uint2 kernelIndex(i, j); 29 | uint2 textureIndex(gid.x + (i - radius), gid.y + (j - radius)); 30 | float4 color = inTexture.read(textureIndex).rgba; 31 | float4 weight = weights.read(kernelIndex).rrrr; 32 | accumColor += weight * color; 33 | } 34 | } 35 | 36 | outTexture.write(float4(accumColor.rgb, 1), gid); 37 | } 38 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/HarrisCornerDetection.metal: -------------------------------------------------------------------------------- 1 | // 2 | // HarrisCornerDetection.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct HarrisCornerDetectionUniforms { 13 | float sensitivity; 14 | }; 15 | 16 | struct Constants { 17 | float harrisConstant = 0.04; 18 | }; 19 | 20 | kernel void harrisCornerDetection(texture2d inTexture [[ texture(0)]], 21 | texture2d outTexture [[ texture(1)]], 22 | constant HarrisCornerDetectionUniforms &uniforms [[ buffer(0) ]], 23 | uint2 gid [[thread_position_in_grid]]) 24 | { 25 | Constants c; 26 | 27 | float3 derivativeElements = inTexture.read(gid).rgb; 28 | float derivativeSum = derivativeElements.x + derivativeElements.y; 29 | float zElement = (derivativeElements.z * 2.0) - 1.0; 30 | float cornerness = derivativeElements.x * derivativeElements.y - (zElement * zElement) - c.harrisConstant * derivativeSum * derivativeSum; 31 | 32 | outTexture.write(float4(float3(cornerness * uniforms.sensitivity), 1.0), gid); 33 | } 34 | 35 | 36 | struct HarrisCornerDetectionOutputUniforms { 37 | 38 | }; 39 | 40 | kernel void harrisCornerDetectionOutput(texture2d inTexture [[ texture(0)]], 41 | texture2d outTexture [[ texture(1)]], 42 | texture2d originalTexture [[ texture(2)]], 43 | constant HarrisCornerDetectionOutputUniforms &uniforms [[ buffer(0) ]], 44 | uint2 gid [[thread_position_in_grid]]) 45 | { 46 | float4 color = originalTexture.read(gid); 47 | float corner = inTexture.read(gid).r; 48 | 49 | if (corner > 0.0) { 50 | color = float4(1, 0, 0, 1); // Change to different color later 51 | outTexture.write(color, gid); 52 | for (int i = 0; i < 3; i++) { 53 | outTexture.write(color, uint2(gid.x, gid.y - i)); 54 | outTexture.write(color, uint2(gid.x + i, gid.y)); 55 | outTexture.write(color, uint2(gid.x, gid.y + i)); 56 | outTexture.write(color, uint2(gid.x - i, gid.y)); 57 | } 58 | } 59 | else { 60 | outTexture.write(color, gid); 61 | } 62 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Haze.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Haze.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct HazeUniforms { 13 | float distance; 14 | float slope; 15 | }; 16 | 17 | kernel void haze(texture2d inTexture [[texture(0)]], 18 | texture2d outTexture [[texture(1)]], 19 | constant HazeUniforms &uniforms [[ buffer(0) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | 23 | float4 color = float4(1.0); 24 | float d = gid.y/inTexture.get_height() * uniforms.slope + uniforms.distance; 25 | float4 c = inTexture.read(gid); 26 | c = (c - d * color) / (1.0 - d); 27 | 28 | outTexture.write(c, gid); 29 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/HighlightShadow.metal: -------------------------------------------------------------------------------- 1 | // 2 | // HighlightShadow.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | constant float3 luminanceWeighting = float3(0.3, 0.3, 0.3); 13 | 14 | struct HighlightShadowUniforms { 15 | float highlights; 16 | float shadows; 17 | }; 18 | 19 | kernel void highlightShadow(texture2d inTexture [[ texture(0)]], 20 | texture2d outTexture [[ texture(1)]], 21 | constant HighlightShadowUniforms &uniforms [[ buffer(0) ]], 22 | uint2 gid [[thread_position_in_grid]]) 23 | { 24 | float4 color = inTexture.read(gid); 25 | 26 | float luminance = dot(color.rgb, luminanceWeighting); 27 | 28 | float shadow = clamp((pow(luminance, 1.0/(uniforms.shadows + 1.0)) + (-0.76) * pow(luminance, 2.0/(uniforms.shadows+1.0))) - luminance, 0.0, 1.0); 29 | float highlight = clamp((1.0 - (pow(1.0 - luminance, 1.0/(2.0 - uniforms.highlights)) + (-0.8)*pow(1.0 - luminance, 2.0/(2.0 - uniforms.highlights)))) - luminance, -1.0, 0.0); 30 | color.rgb = float3(0.0, 0.0, 0.0) + ((luminance + shadow + highlight) - 0.0) * ((color.rgb - float3(0.0, 0.0, 0.0))/(luminance - 0.0)); 31 | 32 | outTexture.write(color, gid); 33 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Histogram.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Histogram.metal 3 | // MTLImage 4 | // 5 | // Created by Mohammad Fathi on 4/25/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct Constants { 13 | float3 W = float3(0.2125, 0.7154, 0.0721); 14 | }; 15 | 16 | struct HistogramUniforms { 17 | float dummy; 18 | }; 19 | 20 | kernel void histogram(texture2d inTexture [[ texture(0) ]], 21 | texture2d outTexture [[ texture(1) ]], 22 | constant HistogramUniforms &uniforms [[ buffer(0) ]], 23 | device float *luminanceBuffer [[ buffer(1) ]], 24 | device float *redBuffer [[ buffer(2) ]], 25 | device float *greenBuffer [[ buffer(3) ]], 26 | device float *blueBuffer [[ buffer(4) ]], 27 | uint2 gid [[thread_position_in_grid]]) 28 | { 29 | 30 | 31 | float4 color = inTexture.read(gid); 32 | 33 | Constants c; 34 | float luminance = dot(color.rgb, c.W); 35 | 36 | luminanceBuffer[int(luminance * 255.0)] += 1.0; 37 | redBuffer [int(color.r * 255.0)] += 1.0; 38 | greenBuffer [int(color.g * 255.0)] += 1.0; 39 | blueBuffer [int(color.b * 255.0)] += 1.0; 40 | 41 | outTexture.write(color, gid); 42 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Hue.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Hue.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct HueUniforms { 13 | float hue; 14 | }; 15 | 16 | constant float4 kRGBToYPrime = float4 (0.299, 0.587, 0.114, 0.0); 17 | constant float4 kRGBToI = float4 (0.595716, -0.274453, -0.321263, 0.0); 18 | constant float4 kRGBToQ = float4 (0.211456, -0.522591, 0.31135, 0.0); 19 | 20 | constant float4 kYIQToR = float4 (1.0, 0.9563, 0.6210, 0.0); 21 | constant float4 kYIQToG = float4 (1.0, -0.2721, -0.6474, 0.0); 22 | constant float4 kYIQToB = float4 (1.0, -1.1070, 1.7046, 0.0); 23 | 24 | kernel void hue(texture2d inTexture [[ texture(0)]], 25 | texture2d outTexture [[ texture(1)]], 26 | constant HueUniforms &uniforms [[ buffer(0) ]], 27 | uint2 gid [[thread_position_in_grid]]) 28 | { 29 | float4 color = inTexture.read(gid); 30 | 31 | float YPrime = dot(color, kRGBToYPrime); 32 | float I = dot(color, kRGBToI); 33 | float Q = dot(color, kRGBToQ); 34 | 35 | float hue = atan2(Q, I); 36 | float chroma = sqrt (I * I + Q * Q); 37 | 38 | hue += (-uniforms.hue); //why negative rotation? 39 | 40 | Q = chroma * sin (hue); 41 | I = chroma * cos (hue); 42 | 43 | float4 yIQ = float4(YPrime, I, Q, 0.0); 44 | color.r = dot (yIQ, kYIQToR); 45 | color.g = dot (yIQ, kYIQToG); 46 | color.b = dot (yIQ, kYIQToB); 47 | 48 | outTexture.write(color, gid); 49 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Invert.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Invert.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | kernel void invert(texture2d inTexture [[ texture(0) ]], 13 | texture2d outTexture [[ texture(1) ]], 14 | uint2 gid [[thread_position_in_grid]]) 15 | { 16 | float4 color = inTexture.read(gid); 17 | outTexture.write(float4(1.0 - color.rgb, color.a), gid); 18 | } 19 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Kuwahara.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Kuwahara.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct KuwaharaUniforms { 13 | float radius; 14 | }; 15 | 16 | kernel void kuwahara(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant KuwaharaUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | // float2 size = float2(1.0 / inTexture.get_width(), 1.0 / inTexture.get_height()); 22 | 23 | float n = float((uniforms.radius + 1) * (uniforms.radius + 1)); 24 | int i; int j; 25 | float3 m0 = float3(0.0); float3 m1 = float3(0.0); float3 m2 = float3(0.0); float3 m3 = float3(0.0); 26 | float3 s0 = float3(0.0); float3 s1 = float3(0.0); float3 s2 = float3(0.0); float3 s3 = float3(0.0); 27 | float3 c; 28 | float radius = uniforms.radius; 29 | 30 | for (j = -radius; j <= 0; ++j) { 31 | for (i = -radius; i <= 0; ++i) { 32 | c = inTexture.read(gid + uint2(i, j)).rgb; 33 | m0 += c; 34 | s0 += c * c; 35 | } 36 | } 37 | 38 | for (j = -radius; j <= 0; ++j) { 39 | for (i = 0; i <= radius; ++i) { 40 | c = inTexture.read(gid + uint2(i, j)).rgb; 41 | m1 += c; 42 | s1 += c * c; 43 | } 44 | } 45 | 46 | for (j = 0; j <= radius; ++j) { 47 | for (i = 0; i <= radius; ++i) { 48 | c = inTexture.read(gid + uint2(i, j)).rgb; 49 | m2 += c; 50 | s2 += c * c; 51 | } 52 | } 53 | 54 | for (j = 0; j <= radius; ++j) { 55 | for (i = -radius; i <= 0; ++i) { 56 | c = inTexture.read(gid + uint2(i, j)).rgb; 57 | m3 += c; 58 | s3 += c * c; 59 | } 60 | } 61 | 62 | float min_sigma2 = 1e+2; 63 | m0 /= n; 64 | s0 = abs(s0 / n - m0 * m0); 65 | 66 | float sigma2 = s0.r + s0.g + s0.b; 67 | if (sigma2 < min_sigma2) { 68 | min_sigma2 = sigma2; 69 | outTexture.write(float4(m0, 1.0), gid); 70 | } 71 | 72 | m1 /= n; 73 | s1 = abs(s1 / n - m1 * m1); 74 | 75 | sigma2 = s1.r + s1.g + s1.b; 76 | if (sigma2 < min_sigma2) { 77 | min_sigma2 = sigma2; 78 | outTexture.write(float4(m1, 1.0), gid); 79 | } 80 | 81 | m2 /= n; 82 | s2 = abs(s2 / n - m2 * m2); 83 | 84 | sigma2 = s2.r + s2.g + s2.b; 85 | if (sigma2 < min_sigma2) { 86 | min_sigma2 = sigma2; 87 | outTexture.write(float4(m2, 1.0), gid); 88 | } 89 | 90 | m3 /= n; 91 | s3 = abs(s3 / n - m3 * m3); 92 | 93 | sigma2 = s3.r + s3.g + s3.b; 94 | if (sigma2 < min_sigma2) { 95 | min_sigma2 = sigma2; 96 | outTexture.write(float4(m3, 1.0), gid); 97 | } 98 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Levels.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Levels.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | #define GammaCorrection(color, gamma) pow(color, 1.0 / gamma) 13 | 14 | #define LevelsControlInputRange(color, minInput, maxInput) min(max(color - minInput, float3(0.0)) / (maxInput - minInput), float3(1.0)) 15 | #define LevelsControlInput(color, minInput, gamma, maxInput) GammaCorrection(LevelsControlInputRange(color, minInput, maxInput), gamma) 16 | #define LevelsControlOutputRange(color, minOutput, maxOutput) mix(minOutput, maxOutput, color) 17 | #define LevelsControl(color, minInput, gamma, maxInput, minOutput, maxOutput) LevelsControlOutputRange(LevelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput) 18 | 19 | struct LevelsUniforms { 20 | float min; 21 | float mid; 22 | float max; 23 | float minOut; 24 | float maxOut; 25 | }; 26 | 27 | kernel void levels(texture2d inTexture [[ texture(0) ]], 28 | texture2d outTexture [[ texture(1) ]], 29 | constant LevelsUniforms &uniforms [[ buffer(0) ]], 30 | uint2 gid [[thread_position_in_grid]]) 31 | { 32 | float4 color = inTexture.read(gid); 33 | outTexture.write(float4(LevelsControl(color.rgb, uniforms.min, 34 | uniforms.mid, uniforms.max, 35 | uniforms.minOut, uniforms.maxOut), color.a), gid); 36 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/LineDetection.metal: -------------------------------------------------------------------------------- 1 | // 2 | // LineDetection.metal 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/9/16. 6 | // 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | 13 | struct LineDetectionUniforms { 14 | float sensitivity; 15 | }; 16 | 17 | kernel void lineDetection(texture2d inTexture [[ texture(0)]], 18 | texture2d outTexture [[ texture(1)]], 19 | constant LineDetectionUniforms &uniforms [[ buffer(0) ]], 20 | device float *accumulatorBuffer [[ buffer(1) ]], 21 | uint2 gid [[thread_position_in_grid]]) 22 | { 23 | float4 color = inTexture.read(gid); 24 | 25 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 26 | // float2 uv = float2(gid)/size; 27 | 28 | // Should be passed in b/w image data. (Sobel Threshold or Canny Threshold) 29 | if (color.r > uniforms.sensitivity) { 30 | for (int i = 1; i <= 180; i++) { 31 | float r = gid.x * cos(float(i)) + gid.y * sin(float(i)); 32 | // float x = float(i)/180.0 * size.x; 33 | // float y = r/10.0 * size.y; 34 | // float c = inTexture.read(uint2(x, y)).x; 35 | // if (c == 1.0) c = 0.0; 36 | // outTexture.write(float4(float3(c + (1.0/180.0)),1), uint2(x, y)); 37 | accumulatorBuffer[uint(r * size.x + i)] += 1; 38 | } 39 | } 40 | 41 | outTexture.write(color, gid); 42 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/LuminanceThreshold.metal: -------------------------------------------------------------------------------- 1 | // 2 | // LuminanceThreshold.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/24/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct Constants { 13 | float3 W = float3(0.2125, 0.7154, 0.0721); 14 | }; 15 | 16 | struct LuminanceThresholdUniforms { 17 | float threshold; 18 | }; 19 | 20 | kernel void luminanceThreshold(texture2d inTexture [[texture(0)]], 21 | texture2d outTexture [[texture(1)]], 22 | constant LuminanceThresholdUniforms &uniforms [[ buffer(0) ]], 23 | uint2 gid [[thread_position_in_grid]]) 24 | { 25 | Constants c; 26 | 27 | float4 color = inTexture.read(gid); 28 | float luminance = dot(color.rgb, c.W); 29 | float thresholdResult = step(uniforms.threshold, luminance); 30 | 31 | outTexture.write(float4(float3(thresholdResult), color.a), gid); 32 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Makefile: -------------------------------------------------------------------------------- 1 | XCODE = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform' 2 | METAL = '$(XCODE)/usr/bin/metal' 3 | METAL_AR = '$(XCODE)/usr/bin/metal-ar' 4 | METAL_LIB = '$(XCODE)/usr/bin/metallib' 5 | SDK = '$(XCODE)/Developer/SDKs/iPhoneOS.sdk' 6 | SRC = $(wildcard *.metal) 7 | OBJ = $(patsubst %.metal, %.air, $(SRC)) 8 | 9 | default: $(SRC) 10 | make clean 11 | make $(OBJ) 12 | $(METAL_AR) r default.metal-ar $(OBJ) 13 | $(METAL_LIB) -o default.metallib default.metal-ar 14 | rm -f *.air *.metal-ar 15 | 16 | %.air: %.metal 17 | $(METAL) -arch air64 -emit-llvm -c -ffast-math -miphoneos-version-min=8.0 -std=ios-metal1.1 $< -o $@ 18 | 19 | %.metal-ar: %.air 20 | $(METAL_AR) r $(OBJ) 21 | metal-ar r $@ $< 22 | 23 | %.metallib: %.metal-ar 24 | metallib -o $@ $< 25 | 26 | clean: 27 | rm -f *.metallib *.metal-ar *.air 28 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Mask.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Mask.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/16/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct MaskUniforms { 13 | float brushSize; 14 | float x; 15 | float y; 16 | }; 17 | 18 | kernel void mask(texture2d inTexture [[ texture(0) ]], 19 | texture2d outTexture [[ texture(1) ]], 20 | texture2d maskTexture [[ texture(2) ]], 21 | texture2d originalTexture [[ texture(3) ]], 22 | constant MaskUniforms &uniforms [[ buffer(0) ]], 23 | uint2 gid [[thread_position_in_grid]]) 24 | { 25 | 26 | 27 | // float4 color = inTexture.read(gid); 28 | 29 | float2 inSize = float2(inTexture.get_width(), inTexture.get_height()); 30 | float2 maskSize = float2(maskTexture.get_width(), maskTexture.get_height()); 31 | // float2 point = float2(uniforms.x * inSize.x, uniforms.y * inSize.y); 32 | 33 | uint2 maskCoord = uint2(float(gid.x)/inSize.x * maskSize.x, float(gid.y)/inSize.y * maskSize.y); 34 | float m = maskTexture.read(maskCoord).r; 35 | // float4 originalColor = originalTexture.read(gid); 36 | 37 | // color = mix(originalColor, color, m); 38 | outTexture.write(float4(float3(m), 1.0), gid); 39 | // outTexture.write(color, gid); 40 | 41 | 42 | 43 | // float m = maskTexture.read(uint2(x,y)).r; 44 | // uint width = uint(uniforms.brushSize); 45 | // for (uint i = gid.x - width/2; i < gid.x + width/2; i++) { 46 | // for (uint j = gid.y - width/2; j < gid.y + width/2; j++) { 47 | // outTexture.write(float4(m, 0.0, 0.0, 1.0), uint2(i, j)); 48 | // } 49 | // } 50 | } 51 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Mosaic.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Mosaic.metal 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/6/16. 6 | // 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct MosaicUniforms { 13 | float intensity; 14 | }; 15 | 16 | kernel void mosaic(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant MosaicUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | 23 | // 24 | // float rand(float co) { return fract(sin(co*(91.3458)) * 47453.5453); } 25 | // const int size = 30; 26 | // vec2 point[size]; 27 | // vec3 color[size]; 28 | // 29 | // float time = iGlobalTime; 30 | // vec2 position = fragCoord.xy / iResolution.xy; 31 | // vec2 uv = (fragCoord.xy/iResolution.xy)-.5; 32 | // uv.x /= iResolution.y/iResolution.x; 33 | // float circle_size = .01; 34 | // float allcircles=0.; float minimum; vec3 output_colors; 35 | // for(int i=0;i 10 | using namespace metal; 11 | 12 | struct PerlinNoiseUniforms { 13 | float scale; 14 | }; 15 | 16 | float4 mod289(float4 x); 17 | float4 permute(float4 x); 18 | float4 taylorInvSqrt(float4 r); 19 | float2 fade(float2 t); 20 | float cnoise(float2 P); 21 | 22 | float4 mod289(float4 x) { 23 | return x - floor(x * (1.0 / 289.0)) * 289.0; 24 | } 25 | 26 | float4 permute(float4 x) { 27 | return mod289(((x*34.0)+1.0)*x); 28 | } 29 | 30 | float4 taylorInvSqrt(float4 r) { 31 | return 1.79284291400159 - 0.85373472095314 * r; 32 | } 33 | 34 | float2 fade(float2 t) { 35 | return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); 36 | } 37 | 38 | // Classic Perlin noise 39 | float cnoise(float2 P) { 40 | float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); 41 | float4 Pf = fract(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); 42 | Pi = mod289(Pi); // To avoid truncation effects in permutation 43 | float4 ix = Pi.xzxz; 44 | float4 iy = Pi.yyww; 45 | float4 fx = Pf.xzxz; 46 | float4 fy = Pf.yyww; 47 | 48 | float4 i = permute(permute(ix) + iy); 49 | 50 | float4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ; 51 | float4 gy = abs(gx) - 0.5 ; 52 | float4 tx = floor(gx + 0.5); 53 | gx = gx - tx; 54 | 55 | float2 g00 = float2(gx.x,gy.x); 56 | float2 g10 = float2(gx.y,gy.y); 57 | float2 g01 = float2(gx.z,gy.z); 58 | float2 g11 = float2(gx.w,gy.w); 59 | 60 | float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); 61 | g00 *= norm.x; 62 | g01 *= norm.y; 63 | g10 *= norm.z; 64 | g11 *= norm.w; 65 | 66 | float n00 = dot(g00, float2(fx.x, fy.x)); 67 | float n10 = dot(g10, float2(fx.y, fy.y)); 68 | float n01 = dot(g01, float2(fx.z, fy.z)); 69 | float n11 = dot(g11, float2(fx.w, fy.w)); 70 | 71 | float2 fade_xy = fade(Pf.xy); 72 | float2 n_x = mix(float2(n00, n01), float2(n10, n11), fade_xy.x); 73 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 74 | return 2.3 * n_xy; 75 | } 76 | 77 | kernel void perlinNoise(texture2d inTexture [[ texture(0) ]], 78 | texture2d outTexture [[ texture(1) ]], 79 | constant PerlinNoiseUniforms &uniforms [[ buffer(0) ]], 80 | uint2 gid [[thread_position_in_grid]]) 81 | { 82 | // float4 color = inTexture.read(gid); 83 | 84 | float n1 = (cnoise(float2(gid) * uniforms.scale) + 1.0) / 2.0; 85 | 86 | float4 colorStart = float4(1, 1, 1, 1); 87 | float4 colorFinish = float4(0, 0, 0, 1); 88 | float4 colorDiff = colorFinish - colorStart; 89 | float4 color = colorStart + colorDiff * n1; 90 | 91 | outTexture.write(color, gid); 92 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Pixellate.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Pixellate.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/1/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct PixellateUniforms { 13 | float dotRadius; 14 | float aspectRatio; 15 | float fractionalWidthOfPixel; 16 | }; 17 | 18 | kernel void pixellate(texture2d inTexture [[ texture(0) ]], 19 | texture2d outTexture [[ texture(1) ]], 20 | constant PixellateUniforms &uniforms [[ buffer(0) ]], 21 | uint2 gid [[thread_position_in_grid]]) 22 | { 23 | 24 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 25 | float2 texCoord = float2(gid.x/size.x, gid.y/size.y); 26 | float aspectRatio = size.y / size.x; 27 | 28 | float width = uniforms.fractionalWidthOfPixel * uniforms.dotRadius; 29 | float2 sampleDivisor = float2(width, width / aspectRatio); 30 | float2 samplePos = texCoord - fmod(texCoord, sampleDivisor) + 0.5 * sampleDivisor; 31 | uint2 sp = uint2(samplePos.x * size.x, samplePos.y * size.y); 32 | 33 | outTexture.write(inTexture.read(sp), gid); 34 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/PolkaDot.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/10/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct PolkaDotUniforms { 13 | float dotRadius; 14 | }; 15 | 16 | kernel void polkaDot(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant PolkaDotUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 22 | float2 index = float2(gid); 23 | float radius = uniforms.dotRadius * size.x/10.0; 24 | float2 closestCenter = index - fmod(index, radius * 2.5) + radius; 25 | float dist = distance(float2(gid), closestCenter); 26 | float within = step(dist, radius); 27 | float4 color = inTexture.read(uint2(closestCenter)); 28 | outTexture.write(float4(color.rgb * within, color.a), gid); 29 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Resize.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Resize.metal 3 | // MTLImage_Example 4 | // 5 | // Created by Mohssen Fathi on 6/12/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | kernel void resize(texture2d inputTexture [[ texture(0) ]], 13 | texture2d outputTexture [[ texture(1) ]], 14 | uint2 gid [[ thread_position_in_grid ]]) 15 | { 16 | 17 | float2 textureSize = float2(inputTexture.get_width(), inputTexture.get_height()); 18 | float2 outputSize = float2(outputTexture.get_width(), outputTexture.get_height()); 19 | float2 texCoord = float2(gid) / textureSize; 20 | uint2 outputGid = uint2(texCoord * outputSize); 21 | 22 | outputTexture.write(inputTexture.read(gid), outputGid); 23 | // outputTexture.write(float4(1.0, 0.0, 0.0, 1.0), outputGid); 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/RollingAverage.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Buffer.metal 3 | // MTLImage_Example 4 | // 5 | // Created by Mohssen Fathi on 6/15/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct RollingAverageUniforms { 13 | float bufferLength; 14 | float currentBufferCount; 15 | }; 16 | 17 | kernel void rollingAverage(texture2d inTexture [[texture(0)]], 18 | texture2d outTexture [[texture(1)]], 19 | texture2d addTexture [[texture(2)]], 20 | texture2d subTexture [[texture(3)]], 21 | constant RollingAverageUniforms &uniforms [[ buffer(0) ]], 22 | uint2 gid [[thread_position_in_grid]]) 23 | { 24 | 25 | float4 color = inTexture.read(gid); 26 | 27 | color += addTexture.read(gid) / uniforms.bufferLength; 28 | 29 | if (uniforms.currentBufferCount >= uniforms.bufferLength) { 30 | color -= subTexture.read(gid) / uniforms.bufferLength; 31 | } 32 | 33 | outTexture.write(color, gid); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Saturation.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Saturation.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 3/10/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SaturationUniforms { 13 | float saturation; 14 | }; 15 | 16 | kernel void saturation(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant SaturationUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float4 color = inTexture.read(gid); 23 | float lum = dot(color.rgb, float3(0.299, 0.587, 0.114)); 24 | float4 satColor = float4(lum, lum, lum, color.a); 25 | 26 | outTexture.write(mix(satColor, color, uniforms.saturation), gid); 27 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Scatter.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Scatter.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/18/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct ScatterUniforms { 13 | float radius; 14 | }; 15 | 16 | //constant float3 lum = float3(0.2125, 0.7154, 0.0721); 17 | 18 | kernel void scatter(texture2d inTexture [[ texture(0)]], 19 | texture2d outTexture [[ texture(1)]], 20 | texture2d noise [[ texture(2)]], 21 | constant ScatterUniforms &uniforms [[ buffer(0) ]], 22 | uint2 gid [[thread_position_in_grid]]) 23 | { 24 | float4 color = inTexture.read(gid); 25 | 26 | // noise.read(uint2(gid.x * uniforms.radius * 2, gid.y * uniforms.radius * 2)).xy 27 | // float l = dot(noise.read(gid).rgb, float3(0.2125, 0.7154, 0.0721)); 28 | 29 | float3 noiseColor = noise.read(gid).rgb; 30 | float l = (1.5 - (noiseColor.r + noiseColor.g + noiseColor.b)) * uniforms.radius; 31 | float2 workingSpaceCoord = float2(gid) - l * 2; 32 | color = inTexture.read(uint2(workingSpaceCoord)); 33 | 34 | // float2 imageSpaceCoord = samplerTransform(image, workingSpaceCoord); 35 | // return sample(image, imageSpaceCoord); 36 | 37 | outTexture.write(color, gid); 38 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Sharpen.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Sharpen.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SharpenUniforms { 13 | float sharpness; 14 | }; 15 | 16 | kernel void sharpen(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | constant SharpenUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float3 textureColor = inTexture.read(gid).rgb; 23 | float3 leftTextureColor = inTexture.read(uint2(gid.x - 1, gid.y )).rgb; 24 | float3 rightTextureColor = inTexture.read(uint2(gid.x + 1, gid.y )).rgb; 25 | float3 topTextureColor = inTexture.read(uint2(gid.x , gid.y - 1)).rgb; 26 | float3 bottomTextureColor = inTexture.read(uint2(gid.x , gid.y + 1)).rgb; 27 | 28 | float centerMultiplier = 1.0 + 4.0 * uniforms.sharpness * (1024.0/inTexture.get_width()); 29 | float edgeMultiplier = uniforms.sharpness * (1024.0/inTexture.get_width()); 30 | 31 | float4 color = float4(textureColor * centerMultiplier - (leftTextureColor * edgeMultiplier + 32 | rightTextureColor * edgeMultiplier + 33 | topTextureColor * edgeMultiplier + 34 | bottomTextureColor * edgeMultiplier), inTexture.read(gid).a); 35 | outTexture.write(color, gid); 36 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Sketch.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Sketch.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SketchUniforms { 13 | float edgeStrength; 14 | }; 15 | 16 | kernel void sketch(texture2d inTexture [[texture(0)]], 17 | texture2d outTexture [[texture(1)]], 18 | constant SketchUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float bottomLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y - 1)).r; 23 | float topRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 1)).r; 24 | float topLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 1)).r; 25 | float bottomRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y - 1)).r; 26 | float leftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 0)).r; 27 | float rightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 0)).r; 28 | float bottomIntensity = inTexture.read(uint2(gid.x + 0, gid.y - 1)).r; 29 | float topIntensity = inTexture.read(uint2(gid.x + 0, gid.y + 1)).r; 30 | 31 | float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity; 32 | float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity; 33 | 34 | float magnitude = 1.0 - length(float2(h, v)) * uniforms.edgeStrength; 35 | 36 | outTexture.write(float4(float3(magnitude), 1.0), gid); 37 | } 38 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Smudge.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Smudge.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/26/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SmudgeUniforms { 13 | float radius; 14 | float x; 15 | float y; 16 | float dx; 17 | float dy; 18 | float force; 19 | }; 20 | 21 | kernel void smudge(texture2d inTexture [[texture(0)]], 22 | texture2d outTexture [[texture(1)]], 23 | constant SmudgeUniforms &uniforms [[ buffer(0) ]], 24 | uint2 gid [[thread_position_in_grid]]) 25 | { 26 | float4 color = inTexture.read(gid); 27 | 28 | float2 newCoord = float2(gid); 29 | float dist = distance(float2(uniforms.x * inTexture.get_width(), uniforms.y * inTexture.get_height()), newCoord); 30 | 31 | if (dist < uniforms.radius) { 32 | float normalisedDistance = 1.0 - (dist / uniforms.radius); 33 | float smoothedDistance = smoothstep(0.0, 1.0, normalisedDistance); 34 | 35 | newCoord = float2(gid) + (float2(uniforms.dx, uniforms.dy) * uniforms.force) * smoothedDistance; 36 | } 37 | 38 | outTexture.write(color, uint2(newCoord)); 39 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/SobelEdgeDetection.metal: -------------------------------------------------------------------------------- 1 | // 2 | // SobelEdgeDetection.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SobelEdgeDetectionUniforms { 13 | float edgeStrength; 14 | }; 15 | 16 | kernel void sobelEdgeDetection(texture2d inTexture [[texture(0)]], 17 | texture2d outTexture [[texture(1)]], 18 | constant SobelEdgeDetectionUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float bottomLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y - 1)).r; 23 | float topRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 1)).r; 24 | float topLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 1)).r; 25 | float bottomRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y - 1)).r; 26 | float leftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 0)).r; 27 | float rightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 0)).r; 28 | float bottomIntensity = inTexture.read(uint2(gid.x + 0, gid.y - 1)).r; 29 | float topIntensity = inTexture.read(uint2(gid.x + 0, gid.y + 1)).r; 30 | 31 | float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity; 32 | float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity; 33 | 34 | float magnitude = length(float2(h, v)) * uniforms.edgeStrength; 35 | 36 | outTexture.write(float4(float3(magnitude), 1.0), gid); 37 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/SobelEdgeDetectionThreshold.metal: -------------------------------------------------------------------------------- 1 | // 2 | // SobelEdgeDetectionThreshold.metal 3 | // Pods 4 | // 5 | // Created by Mohssen Fathi on 5/9/16. 6 | // 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct SobelEdgeDetectionThresholdUniforms { 13 | float threshold; 14 | }; 15 | 16 | kernel void sobelEdgeDetectionThreshold(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant SobelEdgeDetectionThresholdUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | 23 | color.rgb = 1.0 - step(color.rgb, uniforms.threshold); 24 | 25 | outTexture.write(color, gid); 26 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Template.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Template.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/29/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct Uniforms { 13 | 14 | }; 15 | 16 | kernel void t(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant Uniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | float4 color = inTexture.read(gid); 22 | outTexture.write(color, gid); 23 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/ToneCurve.metal: -------------------------------------------------------------------------------- 1 | // 2 | // ToneCurve.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/31/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct ToneCurveUniforms { 13 | 14 | }; 15 | 16 | kernel void toneCurve(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant ToneCurveUniforms &uniforms [[ buffer(0) ]], 19 | device float *toneCurveBuffer [[ buffer(1) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | float4 color = inTexture.read(gid); 23 | 24 | float redCurveValue = float(toneCurveBuffer[int(color.r * 255) * 3 + 0])/255.0; 25 | float greenCurveValue = float(toneCurveBuffer[int(color.g * 255) * 3 + 1])/255.0; 26 | float blueCurveValue = float(toneCurveBuffer[int(color.b * 255) * 3 + 2])/255.0; 27 | 28 | outTexture.write(float4(redCurveValue, greenCurveValue, blueCurveValue, color.a), gid); 29 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Toon.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Toon.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct ToonUniforms { 13 | float quantizationLevels; 14 | float threshold; 15 | }; 16 | 17 | kernel void toon(texture2d inTexture [[texture(0)]], 18 | texture2d outTexture [[texture(1)]], 19 | constant ToonUniforms &uniforms [[ buffer(0) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | 23 | float4 color = inTexture.read(gid); 24 | 25 | float bottomLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y - 1)).r; 26 | float topRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 1)).r; 27 | float topLeftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 1)).r; 28 | float bottomRightIntensity = inTexture.read(uint2(gid.x + 1, gid.y - 1)).r; 29 | float leftIntensity = inTexture.read(uint2(gid.x - 1, gid.y + 0)).r; 30 | float rightIntensity = inTexture.read(uint2(gid.x + 1, gid.y + 0)).r; 31 | float bottomIntensity = inTexture.read(uint2(gid.x + 0, gid.y - 1)).r; 32 | float topIntensity = inTexture.read(uint2(gid.x + 0, gid.y + 1)).r; 33 | 34 | float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity; 35 | float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity; 36 | 37 | float mag = length(float2(h, v)); 38 | float3 posterizedImageColor = floor((color.rgb * uniforms.quantizationLevels) + 0.5) / uniforms.quantizationLevels; 39 | float thresholdTest = 1.0 - step(uniforms.threshold, mag); 40 | 41 | outTexture.write(float4(posterizedImageColor * thresholdTest, color.a), gid); 42 | } 43 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Transform.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Transform.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/17/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct TransformUniforms { 13 | 14 | }; 15 | 16 | kernel void transform(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant TransformUniforms &uniforms [[ buffer(0) ]], 19 | device float *transformBuffer [[ buffer(1) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | // float4x4 transformMatrix = float4x4(float4(transformBuffer[0], transformBuffer[1], transformBuffer[2], transformBuffer[3]), 23 | // float4(transformBuffer[4], transformBuffer[5], transformBuffer[6], transformBuffer[7]), 24 | // float4(transformBuffer[8], transformBuffer[9], transformBuffer[10], transformBuffer[11]), 25 | // float4(transformBuffer[12], transformBuffer[13], transformBuffer[14], transformBuffer[15])); 26 | // 27 | // uint4 i = uint4(transformMatrix * float4(float2(gid), 1, 1)); 28 | // float4 color = inTexture.read(i.xy); 29 | 30 | 31 | float3x3 transformMatrix = float3x3(float3(transformBuffer[0], transformBuffer[1], 0.0), 32 | float3(transformBuffer[2], transformBuffer[3], 0.0), 33 | float3(transformBuffer[4], transformBuffer[5], 1.0)); 34 | 35 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 36 | float2 uv = float2(gid) / size; 37 | 38 | // Set anchor point to center of texture 39 | uv -= float2(0.5); 40 | 41 | float2 i = (transformMatrix * float3(uv, 1)).xy; 42 | 43 | // Undo set anchor point 44 | i += float2(0.5); 45 | 46 | // Adjust size for rotation. Doesn't work 47 | float angle = atan2(transformBuffer[1], transformBuffer[0]); 48 | size = float2(size.x * cos(angle) + size.y * sin(angle), 49 | size.x * sin(angle) + size.y * cos(angle)); 50 | 51 | float4 color = inTexture.read(uint2(i * size)); 52 | 53 | outTexture.write(color, gid); 54 | } 55 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/UnsharpMask.metal: -------------------------------------------------------------------------------- 1 | // 2 | // UnsharpMask.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 6/19/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct UnsharpMaskUniforms { 13 | float intensity; 14 | }; 15 | 16 | kernel void unsharpMask(texture2d inTexture [[ texture(0) ]], 17 | texture2d outTexture [[ texture(1) ]], 18 | texture2d blurTexture [[ texture(2) ]], 19 | constant UnsharpMaskUniforms &uniforms [[ buffer(0) ]], 20 | uint2 gid [[thread_position_in_grid]]) 21 | { 22 | 23 | float4 color = inTexture.read(gid); 24 | float3 blurColor = blurTexture.read(gid).rgb; 25 | 26 | color = float4(color.rgb * uniforms.intensity + blurColor * (1.0 - uniforms.intensity), color.a); 27 | 28 | outTexture.write(color, gid); 29 | } 30 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Vignette.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Vignette.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/8/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct VignetteUniforms { 13 | float x; 14 | float y; 15 | 16 | float r; 17 | float g; 18 | float b; 19 | 20 | float start; 21 | float end; 22 | }; 23 | 24 | kernel void vignette(texture2d inTexture [[ texture(0) ]], 25 | texture2d outTexture [[ texture(1) ]], 26 | constant VignetteUniforms &uniforms [[ buffer(0) ]], 27 | uint2 gid [[thread_position_in_grid]]) 28 | { 29 | 30 | float4 color = inTexture.read(gid); 31 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 32 | float2 point = float2(gid.x/size.x, gid.y/size.y); 33 | float2 center = float2(uniforms.x, uniforms.y); 34 | float d = distance(point, center); 35 | float percent = smoothstep(uniforms.start, uniforms.end, d); 36 | float3 vignetteColor = float3(uniforms.r, uniforms.g, uniforms.b); 37 | 38 | outTexture.write(float4(mix(color.rgb, vignetteColor, percent), color.a), gid); 39 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/Water.metal: -------------------------------------------------------------------------------- 1 | // 2 | // Water.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/9/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | float col(float2 coordinate, float time, float speed, float frequency, float intensity); 13 | 14 | struct WaterUniforms { 15 | float time; 16 | float speed; 17 | float frequency; 18 | float intensity; 19 | float emboss; 20 | float delta; 21 | float intence; 22 | }; 23 | 24 | struct Constants { 25 | const int steps = 6; 26 | const float PI = 3.1415926535897932; 27 | 28 | const int angle = 9; 29 | const float speed_x = 0.3; 30 | const float speed_y = 0.3; 31 | 32 | const float reflectionCutOff = 0.012; 33 | const float reflectionIntence = 200000.; 34 | }; 35 | 36 | 37 | float col(float2 coordinate, float time, float speed, float frequency, float intensity) { 38 | Constants c; 39 | 40 | float delta_theta = 2.0 *c.PI / float(c.angle); 41 | float col = 0.0; 42 | float theta = 0.0; 43 | for (int i = 0; i < c.steps; i++) { 44 | float2 adjc = coordinate; 45 | theta = delta_theta * float(i); 46 | adjc.x += cos(theta) * time * speed + time * c.speed_x; 47 | adjc.y -= sin(theta) * time * speed - time * c.speed_y; 48 | col = col + cos( (adjc.x * cos(theta) - adjc.y * sin(theta)) * frequency) * intensity; 49 | } 50 | 51 | return cos(col); 52 | } 53 | 54 | kernel void water(texture2d inTexture [[ texture(0) ]], 55 | texture2d outTexture [[ texture(1) ]], 56 | constant WaterUniforms &uniforms [[ buffer(0) ]], 57 | uint2 gid [[thread_position_in_grid]]) 58 | { 59 | Constants c; 60 | 61 | float2 size = float2(inTexture.get_width(), inTexture.get_height()); 62 | float2 textureCoordinate = float2(gid.x/size.x, gid.y/size.y); 63 | float2 p = textureCoordinate.xy; 64 | float2 c1 = p; 65 | float2 c2 = p; 66 | float cc1 = col(c1, uniforms.time, uniforms.speed, uniforms.frequency, uniforms.intensity); 67 | 68 | c2.x += textureCoordinate.x/uniforms.delta; 69 | float cc2 = col(c2, uniforms.time, uniforms.speed, uniforms.frequency, uniforms.intensity); 70 | float dx = uniforms.emboss * (cc1 - cc2)/uniforms.delta; 71 | 72 | c2.x = p.x; 73 | c2.y += textureCoordinate.y/uniforms.delta; 74 | cc2 = col(c2, uniforms.time, uniforms.speed, uniforms.frequency, uniforms.intensity); 75 | float dy = uniforms.emboss * (cc1-cc2)/uniforms.delta; 76 | 77 | c1.x += dx * 2.; 78 | // c1.y = -(c1.y + dy * 2.); 79 | 80 | float alpha = 1.0 + dx * dy * uniforms.intence; 81 | 82 | float ddx = dx - c.reflectionCutOff; 83 | float ddy = dy - c.reflectionCutOff; 84 | if (ddx > 0. && ddy > 0.) { 85 | alpha = pow(alpha, ddx * ddy * c.reflectionIntence); 86 | } 87 | 88 | uint2 g = uint2(c1.x * size.x, c1.y * size.y); 89 | float4 color = inTexture.read(g); 90 | outTexture.write(color, gid); 91 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/WeakPixelInclusion.metal: -------------------------------------------------------------------------------- 1 | // 2 | // WeakPixelInclusion.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct WeakPixelInclusionUniforms { 13 | 14 | }; 15 | 16 | kernel void weakPixelInclusion(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant WeakPixelInclusionUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float center = inTexture.read(gid ).r; 23 | float bottom = inTexture.read(uint2(gid.x , gid.y - 1)).r; 24 | float bottomLeft = inTexture.read(uint2(gid.x - 1, gid.y - 1)).r; 25 | float bottomRight = inTexture.read(uint2(gid.x + 1, gid.y - 1)).r; 26 | float left = inTexture.read(uint2(gid.x - 1, gid.y )).r; 27 | float right = inTexture.read(uint2(gid.x + 1, gid.y )).r; 28 | float top = inTexture.read(uint2(gid.x , gid.y + 1)).r; 29 | float topRight = inTexture.read(uint2(gid.x + 1, gid.y + 1)).r; 30 | float topLeft = inTexture.read(uint2(gid.x - 1, gid.y + 1)).r; 31 | 32 | float pixelSum = bottomLeft + topRight + topLeft + bottomRight + left + right + bottom + top + center; 33 | float sumTest = step(1.5, pixelSum); 34 | float pixelTest = step(0.01, center); 35 | 36 | outTexture.write(float4(float3(sumTest * pixelTest), 1.0), gid); 37 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/WhiteBalance.metal: -------------------------------------------------------------------------------- 1 | // 2 | // WhiteBalance.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 4/2/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct WhiteBalanceUniforms { 13 | float temperature; 14 | float tint; 15 | }; 16 | 17 | struct Constants { 18 | float3 warmFilter = float3(0.93, 0.54, 0.0); 19 | float3x3 RGBtoYIQ = float3x3(float3(0.299, 0.587, 0.114), float3(0.596, -0.274, -0.322), float3(0.212, -0.523, 0.311)); 20 | float3x3 YIQtoRGB = float3x3(float3(1.000, 0.956, 0.621), float3(1.000, -0.272, -0.647), float3(1.000, -1.105, 1.702)); 21 | }; 22 | 23 | kernel void whiteBalance(texture2d inTexture [[ texture(0) ]], 24 | texture2d outTexture [[ texture(1) ]], 25 | constant WhiteBalanceUniforms &uniforms [[ buffer(0) ]], 26 | uint2 gid [[thread_position_in_grid]]) 27 | { 28 | 29 | Constants c; 30 | float4 color = inTexture.read(gid); 31 | 32 | float3 yiq = c.RGBtoYIQ * color.rgb; 33 | yiq.b = clamp(yiq.b + uniforms.tint * 0.5226 * 0.1, -0.5226, 0.5226); 34 | float3 rgb = c.YIQtoRGB * yiq; 35 | 36 | float3 processed = float3((rgb.r < 0.5 ? (2.0 * rgb.r * c.warmFilter.r) : (1.0 - 2.0 * (1.0 - rgb.r) * (1.0 - c.warmFilter.r))), 37 | (rgb.g < 0.5 ? (2.0 * rgb.g * c.warmFilter.g) : (1.0 - 2.0 * (1.0 - rgb.g) * (1.0 - c.warmFilter.g))), 38 | (rgb.b < 0.5 ? (2.0 * rgb.b * c.warmFilter.b) : (1.0 - 2.0 * (1.0 - rgb.b) * (1.0 - c.warmFilter.b)))); 39 | 40 | outTexture.write(float4(mix(rgb, processed, uniforms.temperature), color.a), gid); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/XYDerivative.metal: -------------------------------------------------------------------------------- 1 | // 2 | // XYDerivative.metal 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 5/11/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct XYDerivativeUniforms { 13 | float edgeStrength; 14 | }; 15 | 16 | kernel void xyDerivative(texture2d inTexture [[ texture(0)]], 17 | texture2d outTexture [[ texture(1)]], 18 | constant XYDerivativeUniforms &uniforms [[ buffer(0) ]], 19 | uint2 gid [[thread_position_in_grid]]) 20 | { 21 | 22 | float bottom = inTexture.read(uint2(gid.x , gid.y - 1)).r; 23 | float bottomLeft = inTexture.read(uint2(gid.x - 1, gid.y - 1)).r; 24 | float bottomRight = inTexture.read(uint2(gid.x + 1, gid.y - 1)).r; 25 | float left = inTexture.read(uint2(gid.x - 1, gid.y )).r; 26 | float right = inTexture.read(uint2(gid.x + 1, gid.y )).r; 27 | float top = inTexture.read(uint2(gid.x , gid.y + 1)).r; 28 | float topRight = inTexture.read(uint2(gid.x + 1, gid.y + 1)).r; 29 | float topLeft = inTexture.read(uint2(gid.x - 1, gid.y + 1)).r; 30 | 31 | float verticalDerivative = -topLeft - top - topRight + bottomLeft + bottom + bottomRight; 32 | float horizontalDerivative = -bottomLeft - left - topLeft + bottomRight + right + topRight; 33 | verticalDerivative = verticalDerivative * uniforms.edgeStrength; 34 | horizontalDerivative = horizontalDerivative * uniforms.edgeStrength; 35 | 36 | // Scaling the X * Y operation so that negative numbers are not clipped in the 0..1 range. This will be expanded in the corner detection filter 37 | float4 color = float4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, 38 | ((verticalDerivative * horizontalDerivative) + 1.0) / 2.0, 1.0); 39 | 40 | 41 | outTexture.write(color, gid); 42 | } -------------------------------------------------------------------------------- /MTLImage/Sources/Shaders/default.metallib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohssenfathi/MTLImage/20119eed9d07f421939d43d660c9505cdedb3492/MTLImage/Sources/Shaders/default.metallib -------------------------------------------------------------------------------- /MTLImage/Sources/Tools/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // MTLImage-iOS10.0 4 | // 5 | // Created by Mohssen Fathi on 6/9/17. 6 | // 7 | 8 | extension MTLTexture { 9 | 10 | func size() -> MTLSize { 11 | return MTLSize(width: width, height: height, depth: depth) 12 | } 13 | } 14 | 15 | func *(left: CGSize, right: CGFloat) -> CGSize { 16 | return CGSize(width: left.width * right, height: left.height * right) 17 | } 18 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Package.swift 3 | // MTLImage 4 | // 5 | // Created by Mohssen Fathi on 8/22/17. 6 | // 7 | 8 | import PackageDescription 9 | 10 | let package = Package( 11 | name: "MTLImage", 12 | dependencies : [], 13 | exclude: ["Tests"] 14 | ) 15 | 16 | --------------------------------------------------------------------------------