diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b70efca --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Exclude the build directory +build/* +examples/FilterShowcase/build* + +# Exclude temp nibs and swap files +*~.nib +*.swp + +# Exclude OS X folder attributes +.DS_Store +.svn + +# Exclude user-specific XCode 3 and 4 files +*.mode1 +*.mode1v3 +*.mode2v3 +*.perspective +*.perspectivev3 +*.pbxuser +*.xcworkspace +xcuserdata + +# Documentation +documentation/* + +# Carthage +Carthage/Build diff --git a/License.txt b/License.txt new file mode 100755 index 0000000..3cedcf2 --- /dev/null +++ b/License.txt @@ -0,0 +1,9 @@ +Copyright (c) 2018, Brad Larson and Janie Clayton. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of the GPUImage framework nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 9726313..5c8601b --- a/README.md +++ b/README.md @@ -1,2 +1,229 @@ -# GPUImage3 -GPUImage 3 is a BSD-licensed Swift framework for GPU-accelerated video and image processing using Metal. +# GPUImage 3 # + +
+ +Brad Larson + +http://www.sunsetlakesoftware.com + +[@bradlarson](http://twitter.com/bradlarson) + +contact@sunsetlakesoftware.com + +Janie Clayton + +http://redqueengraphics.com + +[@RedQueenCoder](https://twitter.com/RedQueenCoder) + +## Overview ## + +GPUImage 3 is the third generation of the GPUImage framework, an open source project for performing GPU-accelerated image and video processing on Mac and iOS. The original GPUImage framework was written in Objective-C and targeted Mac and iOS, the second iteration rewritten in Swift using OpenGL to target Mac, iOS, and Linux, and now this third generation is redesigned to use Metal in place of OpenGL. + +The objective of the framework is to make it as easy as possible to set up and perform realtime video processing or machine vision against image or video sources. [Something about Metal here] + +## License ## + +BSD-style, with the full license available with the framework in License.txt. + +## Technical requirements ## + +- Swift 4 +- Xcode 9.0 on Mac or iOS +- iOS: 8.0 or higher (Swift is supported on 7.0, but not Mac-style frameworks) +- OSX: 10.9 or higher + +## General architecture ## + +The framework relies on the concept of a processing pipeline, where image sources are targeted at image consumers, and so on down the line until images are output to the screen, to image files, to raw data, or to recorded movies. Cameras, movies, still images, and raw data can be inputs into this pipeline. Arbitrarily complex processing operations can be built from a combination of a series of smaller operations. + +This is an object-oriented framework, with classes that encapsulate inputs, processing operations, and outputs. The processing operations use Metal vertex and fragment shaders to perform their image manipulations on the GPU. + +Examples for usage of the framework in common applications are shown below. + +## Using GPUImage in a Mac or iOS application ## + +To add the GPUImage framework to your Mac or iOS application, either drag the GPUImage.xcodeproj project into your application's project or add it via File | Add Files To... + +After that, go to your project's Build Phases and add GPUImage_iOS or GPUImage_macOS as a Target Dependency. Add it to the Link Binary With Libraries phase. Add a new Copy Files build phase, set its destination to Frameworks, and add the upper GPUImage.framework (for Mac) or lower GPUImage.framework (for iOS) to that. That last step will make sure the framework is deployed in your application bundle. + +In any of your Swift files that reference GPUImage classes, simply add + +```swift +import GPUImage +``` + +and you should be ready to go. + +Note that you may need to build your project once to parse and build the GPUImage framework in order for Xcode to stop warning you about the framework and its classes being missing. + +## Performing common tasks ## + +### Filtering live video ### + +To filter live video from a Mac or iOS camera, you can write code like the following: + +```swift +do { + camera = try Camera(sessionPreset:AVCaptureSessionPreset640x480) + filter = SaturationAdjustment() + camera --> filter --> renderView + camera.startCapture() +} catch { + fatalError("Could not initialize rendering pipeline: \(error)") +} +``` + +where renderView is an instance of RenderView that you've placed somewhere in your view hierarchy. The above instantiates a 640x480 camera instance, creates a saturation filter, and directs camera frames to be processed through the saturation filter on their way to the screen. startCapture() initiates the camera capture process. + +The --> operator chains an image source to an image consumer, and many of these can be chained in the same line. + +### Capturing and filtering a still photo ### + +Functionality not completed. + +### Capturing an image from video ### + +(Not currently available on Linux.) + +To capture a still image from live video, you need to set a callback to be performed on the next frame of video that is processed. The easiest way to do this is to use the convenience extension to capture, encode, and save a file to disk: + +```swift +filter.saveNextFrameToURL(url, format:.PNG) +``` + +Under the hood, this creates a PictureOutput instance, attaches it as a target to your filter, sets the PictureOutput's encodedImageFormat to PNG files, and sets the encodedImageAvailableCallback to a closure that takes in the data for the filtered image and saves it to a URL. + +You can set this up manually to get the encoded image data (as NSData): + +```swift +let pictureOutput = PictureOutput() +pictureOutput.encodedImageFormat = .JPEG +pictureOutput.encodedImageAvailableCallback = {imageData in + // Do something with the NSData +} +filter --> pictureOutput +``` + +You can also get the filtered image in a platform-native format (NSImage, UIImage) by setting the imageAvailableCallback: + +```swift +let pictureOutput = PictureOutput() +pictureOutput.encodedImageFormat = .JPEG +pictureOutput.imageAvailableCallback = {image in + // Do something with the image +} +filter --> pictureOutput +``` + +### Processing a still image ### + +(Not currently available on Linux.) + +There are a few different ways to approach filtering an image. The easiest are the convenience extensions to UIImage or NSImage that let you filter that image and return a UIImage or NSImage: + +```swift +let testImage = UIImage(named:"WID-small.jpg")! +let toonFilter = SmoothToonFilter() +let filteredImage = testImage.filterWithOperation(toonFilter) +``` + +for a more complex pipeline: + +```swift +let testImage = UIImage(named:"WID-small.jpg")! +let toonFilter = SmoothToonFilter() +let luminanceFilter = Luminance() +let filteredImage = testImage.filterWithPipeline{input, output in + input --> toonFilter --> luminanceFilter --> output +} +``` + +One caution: if you want to display an image to the screen or repeatedly filter an image, don't use these methods. Going to and from Core Graphics adds a lot of overhead. Instead, I recommend manually setting up a pipeline and directing it to a RenderView for display in order to keep everything on the GPU. + +Both of these convenience methods wrap several operations. To feed a picture into a filter pipeline, you instantiate a PictureInput. To capture a picture from the pipeline, you use a PictureOutput. To manually set up processing of an image, you can use something like the following: + +```swift +let toonFilter = SmoothToonFilter() +let testImage = UIImage(named:"WID-small.jpg")! +let pictureInput = PictureInput(image:testImage) +let pictureOutput = PictureOutput() +pictureOutput.imageAvailableCallback = {image in + // Do something with image +} +pictureInput --> toonFilter --> pictureOutput +pictureInput.processImage(synchronously:true) +``` + +In the above, the imageAvailableCallback will be triggered right at the processImage() line. If you want the image processing to be done asynchronously, leave out the synchronously argument in the above. + +### Filtering and re-encoding a movie ### + +To filter an existing movie file, you can write code like the following: + +```swift + +do { + let bundleURL = Bundle.main.resourceURL! + let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! + movie = try MovieInput(url:movieURL, playAtActualSpeed:true) + filter = SaturationAdjustment() + movie --> filter --> renderView + movie.start() +} catch { + fatalError("Could not initialize rendering pipeline: \(error)") +} +``` + +where renderView is an instance of RenderView that you've placed somewhere in your view hierarchy. The above loads a movie named "sample_iPod.m4v" from the application's bundle, creates a saturation filter, and directs movie frames to be processed through the saturation filter on their way to the screen. start() initiates the movie playback. + +### Writing a custom image processing operation ### + +The framework uses a series of protocols to define types that can output images to be processed, take in an image for processing, or do both. These are the ImageSource, ImageConsumer, and ImageProcessingOperation protocols, respectively. Any type can comply to these, but typically classes are used. + +Many common filters and other image processing operations can be described as subclasses of the BasicOperation class. BasicOperation provides much of the internal code required for taking in an image frame from one or more inputs, rendering a rectangular image (quad) from those inputs using a specified shader program, and providing that image to all of its targets. Variants on BasicOperation, such as TextureSamplingOperation or TwoStageOperation, provide additional information to the shader program that may be needed for certain kinds of operations. + +To build a simple, one-input filter, you may not even need to create a subclass of your own. All you need to do is supply a fragment shader and the number of inputs needed when instantiating a BasicOperation: + +```swift +let myFilter = BasicOperation(fragmentShaderFile:MyFilterFragmentShaderURL, numberOfInputs:1) +``` + +A shader program is composed of matched vertex and fragment shaders that are compiled and linked together into one program. By default, the framework uses a series of stock vertex shaders based on the number of input images feeding into an operation. Usually, all you'll need to do is provide the custom fragment shader that is used to perform your filtering or other processing. + +Fragment shaders used by GPUImage look something like this: + +[TODO: Rework for Metal operations] + +### Grouping operations ### + +If you wish to group a series of operations into a single unit to pass around, you can create a new instance of OperationGroup. OperationGroup provides a configureGroup property that takes a closure which specifies how the group should be configured: + +```swift +let boxBlur = BoxBlur() +let contrast = ContrastAdjustment() + +let myGroup = OperationGroup() + +myGroup.configureGroup{input, output in + input --> self.boxBlur --> self.contrast --> output +} +``` + +Frames coming in to the OperationGroup are represented by the input in the above closure, and frames going out of the entire group by the output. After setup, myGroup in the above will appear like any other operation, even though it is composed of multiple sub-operations. This group can then be passed or worked with like a single operation. + +### Interacting with Metal ### + +[TODO: Rework for Metal] + +## Common types ## + +The framework uses several platform-independent types to represent common values. Generally, floating-point inputs are taken in as Floats. Sizes are specified using Size types (constructed by initializing with width and height). Colors are handled via the Color type, where you provide the normalized-to-1.0 color values for red, green, blue, and optionally alpha components. + +Positions can be provided in 2-D and 3-D coordinates. If a Position is created by only specifying X and Y values, it will be handled as a 2-D point. If an optional Z coordinate is also provided, it will be dealt with as a 3-D point. + +Matrices come in Matrix3x3 and Matrix4x4 varieties. These matrices can be build using a row-major array of Floats, or can be initialized from CATransform3D or CGAffineTransform structs. + +## Built-in operations ## + +[TODO: Fill in with operations as they are added] \ No newline at end of file diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fc44d9e --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj @@ -0,0 +1,427 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + BCE0BEB720D6E8C20006E120 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BEB620D6E8C20006E120 /* AppDelegate.swift */; }; + BCE0BEB920D6E8C20006E120 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BEB820D6E8C20006E120 /* ViewController.swift */; }; + BCE0BEBC20D6E8C20006E120 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BCE0BEBA20D6E8C20006E120 /* Main.storyboard */; }; + BCE0BEBE20D6E8C40006E120 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BCE0BEBD20D6E8C40006E120 /* Assets.xcassets */; }; + BCE0BEC120D6E8C40006E120 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BCE0BEBF20D6E8C40006E120 /* LaunchScreen.storyboard */; }; + BCE0BED520D6E92E0006E120 /* GPUImage.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCE0BECF20D6E90F0006E120 /* GPUImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BCE0BECE20D6E90F0006E120 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BCE0BE7C20D6DE610006E120; + remoteInfo = GPUImage_iOS; + }; + BCE0BED020D6E90F0006E120 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BCE0BE8C20D6E2B80006E120; + remoteInfo = GPUImage_macOS; + }; + BCE0BED220D6E91A0006E120 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = BCE0BE7B20D6DE610006E120; + remoteInfo = GPUImage_iOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BCE0BED420D6E9240006E120 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + BCE0BED520D6E92E0006E120 /* GPUImage.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BCE0BEB320D6E8C20006E120 /* SimpleVideoFilter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleVideoFilter.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BCE0BEB620D6E8C20006E120 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + BCE0BEB820D6E8C20006E120 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + BCE0BEBB20D6E8C20006E120 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + BCE0BEBD20D6E8C40006E120 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BCE0BEC020D6E8C40006E120 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BCE0BEC220D6E8C40006E120 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GPUImage.xcodeproj; path = ../../../framework/GPUImage.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BCE0BEB020D6E8C20006E120 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BCE0BEAA20D6E8C20006E120 = { + isa = PBXGroup; + children = ( + BCE0BEB520D6E8C20006E120 /* SimpleVideoFilter */, + BCE0BEC820D6E8F40006E120 /* Frameworks */, + BCE0BEB420D6E8C20006E120 /* Products */, + ); + sourceTree = ""; + }; + BCE0BEB420D6E8C20006E120 /* Products */ = { + isa = PBXGroup; + children = ( + BCE0BEB320D6E8C20006E120 /* SimpleVideoFilter.app */, + ); + name = Products; + sourceTree = ""; + }; + BCE0BEB520D6E8C20006E120 /* SimpleVideoFilter */ = { + isa = PBXGroup; + children = ( + BCE0BEB620D6E8C20006E120 /* AppDelegate.swift */, + BCE0BEB820D6E8C20006E120 /* ViewController.swift */, + BCE0BEBA20D6E8C20006E120 /* Main.storyboard */, + BCE0BEBD20D6E8C40006E120 /* Assets.xcassets */, + BCE0BEBF20D6E8C40006E120 /* LaunchScreen.storyboard */, + BCE0BEC220D6E8C40006E120 /* Info.plist */, + ); + path = SimpleVideoFilter; + sourceTree = ""; + }; + BCE0BEC820D6E8F40006E120 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + BCE0BECA20D6E90E0006E120 /* Products */ = { + isa = PBXGroup; + children = ( + BCE0BECF20D6E90F0006E120 /* GPUImage.framework */, + BCE0BED120D6E90F0006E120 /* GPUImage.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BCE0BEB220D6E8C20006E120 /* SimpleVideoFilter */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCE0BEC520D6E8C40006E120 /* Build configuration list for PBXNativeTarget "SimpleVideoFilter" */; + buildPhases = ( + BCE0BEAF20D6E8C20006E120 /* Sources */, + BCE0BEB020D6E8C20006E120 /* Frameworks */, + BCE0BEB120D6E8C20006E120 /* Resources */, + BCE0BED420D6E9240006E120 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + BCE0BED320D6E91A0006E120 /* PBXTargetDependency */, + ); + name = SimpleVideoFilter; + productName = SimpleVideoFilter; + productReference = BCE0BEB320D6E8C20006E120 /* SimpleVideoFilter.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BCE0BEAB20D6E8C20006E120 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "Red Queen Coder, LLC"; + TargetAttributes = { + BCE0BEB220D6E8C20006E120 = { + CreatedOnToolsVersion = 9.3; + }; + }; + }; + buildConfigurationList = BCE0BEAE20D6E8C20006E120 /* Build configuration list for PBXProject "SimpleVideoFilter" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BCE0BEAA20D6E8C20006E120; + productRefGroup = BCE0BEB420D6E8C20006E120 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = BCE0BECA20D6E90E0006E120 /* Products */; + ProjectRef = BCE0BEC920D6E90E0006E120 /* GPUImage.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + BCE0BEB220D6E8C20006E120 /* SimpleVideoFilter */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + BCE0BECF20D6E90F0006E120 /* GPUImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = GPUImage.framework; + remoteRef = BCE0BECE20D6E90F0006E120 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + BCE0BED120D6E90F0006E120 /* GPUImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = GPUImage.framework; + remoteRef = BCE0BED020D6E90F0006E120 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + BCE0BEB120D6E8C20006E120 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCE0BEC120D6E8C40006E120 /* LaunchScreen.storyboard in Resources */, + BCE0BEBE20D6E8C40006E120 /* Assets.xcassets in Resources */, + BCE0BEBC20D6E8C20006E120 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BCE0BEAF20D6E8C20006E120 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCE0BEB920D6E8C20006E120 /* ViewController.swift in Sources */, + BCE0BEB720D6E8C20006E120 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BCE0BED320D6E91A0006E120 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GPUImage_iOS; + targetProxy = BCE0BED220D6E91A0006E120 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BCE0BEBA20D6E8C20006E120 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BCE0BEBB20D6E8C20006E120 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + BCE0BEBF20D6E8C40006E120 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BCE0BEC020D6E8C40006E120 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BCE0BEC320D6E8C40006E120 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BCE0BEC420D6E8C40006E120 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + BCE0BEC620D6E8C40006E120 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SimpleVideoFilter/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.SimpleVideoFilter; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BCE0BEC720D6E8C40006E120 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SimpleVideoFilter/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.SimpleVideoFilter; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BCE0BEAE20D6E8C20006E120 /* Build configuration list for PBXProject "SimpleVideoFilter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCE0BEC320D6E8C40006E120 /* Debug */, + BCE0BEC420D6E8C40006E120 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCE0BEC520D6E8C40006E120 /* Build configuration list for PBXNativeTarget "SimpleVideoFilter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCE0BEC620D6E8C40006E120 /* Debug */, + BCE0BEC720D6E8C40006E120 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BCE0BEAB20D6E8C20006E120 /* Project object */; +} diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/AppDelegate.swift b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/AppDelegate.swift new file mode 100644 index 0000000..ecede7f --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/AppDelegate.swift @@ -0,0 +1,38 @@ +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // 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. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // 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. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // 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. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/Contents.json b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/LaunchScreen.storyboard b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f83f6fd --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/Main.storyboard b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/Main.storyboard new file mode 100644 index 0000000..03c13c2 --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Info.plist b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Info.plist new file mode 100644 index 0000000..16be3b6 --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/ViewController.swift b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/ViewController.swift new file mode 100644 index 0000000..bd5476a --- /dev/null +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter/ViewController.swift @@ -0,0 +1,18 @@ +import UIKit +import GPUImage + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/framework/GPUImage.xcodeproj/GPUImage_Info.plist b/framework/GPUImage.xcodeproj/GPUImage_Info.plist new file mode 100644 index 0000000..bc59ff9 --- /dev/null +++ b/framework/GPUImage.xcodeproj/GPUImage_Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + NSHumanReadableCopyright + Copyright © 2018 Red Queen Coder, LLC. All rights reserved. + + diff --git a/framework/GPUImage.xcodeproj/project.pbxproj b/framework/GPUImage.xcodeproj/project.pbxproj new file mode 100644 index 0000000..94b69c9 --- /dev/null +++ b/framework/GPUImage.xcodeproj/project.pbxproj @@ -0,0 +1,508 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + BCE0BE9E20D6E3C80006E120 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9820D6E3C80006E120 /* Color.swift */; }; + BCE0BE9F20D6E3C80006E120 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9820D6E3C80006E120 /* Color.swift */; }; + BCE0BEA020D6E3C80006E120 /* Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9920D6E3C80006E120 /* Size.swift */; }; + BCE0BEA120D6E3C80006E120 /* Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9920D6E3C80006E120 /* Size.swift */; }; + BCE0BEA220D6E3C80006E120 /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9A20D6E3C80006E120 /* Position.swift */; }; + BCE0BEA320D6E3C80006E120 /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9A20D6E3C80006E120 /* Position.swift */; }; + BCE0BEA420D6E3C80006E120 /* Pipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9B20D6E3C80006E120 /* Pipeline.swift */; }; + BCE0BEA520D6E3C80006E120 /* Pipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9B20D6E3C80006E120 /* Pipeline.swift */; }; + BCE0BEA620D6E3C80006E120 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9C20D6E3C80006E120 /* Matrix.swift */; }; + BCE0BEA720D6E3C80006E120 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9C20D6E3C80006E120 /* Matrix.swift */; }; + BCE0BEA820D6E3C80006E120 /* ImageOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9D20D6E3C80006E120 /* ImageOrientation.swift */; }; + BCE0BEA920D6E3C80006E120 /* ImageOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE0BE9D20D6E3C80006E120 /* ImageOrientation.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + BCE0BE7C20D6DE610006E120 /* GPUImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GPUImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BCE0BE8C20D6E2B80006E120 /* GPUImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GPUImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BCE0BE9820D6E3C80006E120 /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = Source/Color.swift; sourceTree = ""; }; + BCE0BE9920D6E3C80006E120 /* Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Size.swift; path = Source/Size.swift; sourceTree = ""; }; + BCE0BE9A20D6E3C80006E120 /* Position.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Position.swift; path = Source/Position.swift; sourceTree = ""; }; + BCE0BE9B20D6E3C80006E120 /* Pipeline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Pipeline.swift; path = Source/Pipeline.swift; sourceTree = ""; }; + BCE0BE9C20D6E3C80006E120 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Matrix.swift; path = Source/Matrix.swift; sourceTree = ""; }; + BCE0BE9D20D6E3C80006E120 /* ImageOrientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageOrientation.swift; path = Source/ImageOrientation.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BCE0BE7820D6DE610006E120 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCE0BE8820D6E2B80006E120 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BCE0BE7220D6DE610006E120 = { + isa = PBXGroup; + children = ( + BCE0BE9420D6E37D0006E120 /* Base */, + BCE0BE9520D6E3830006E120 /* Inputs */, + BCE0BE9620D6E38F0006E120 /* Outputs */, + BCE0BE9720D6E39E0006E120 /* Operations */, + BCE0BE7D20D6DE610006E120 /* Products */, + ); + sourceTree = ""; + }; + BCE0BE7D20D6DE610006E120 /* Products */ = { + isa = PBXGroup; + children = ( + BCE0BE7C20D6DE610006E120 /* GPUImage.framework */, + BCE0BE8C20D6E2B80006E120 /* GPUImage.framework */, + ); + name = Products; + sourceTree = ""; + }; + BCE0BE9420D6E37D0006E120 /* Base */ = { + isa = PBXGroup; + children = ( + BCE0BE9B20D6E3C80006E120 /* Pipeline.swift */, + BCE0BE9D20D6E3C80006E120 /* ImageOrientation.swift */, + BCE0BE9820D6E3C80006E120 /* Color.swift */, + BCE0BE9A20D6E3C80006E120 /* Position.swift */, + BCE0BE9920D6E3C80006E120 /* Size.swift */, + BCE0BE9C20D6E3C80006E120 /* Matrix.swift */, + ); + name = Base; + sourceTree = ""; + }; + BCE0BE9520D6E3830006E120 /* Inputs */ = { + isa = PBXGroup; + children = ( + ); + name = Inputs; + sourceTree = ""; + }; + BCE0BE9620D6E38F0006E120 /* Outputs */ = { + isa = PBXGroup; + children = ( + ); + name = Outputs; + sourceTree = ""; + }; + BCE0BE9720D6E39E0006E120 /* Operations */ = { + isa = PBXGroup; + children = ( + ); + name = Operations; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + BCE0BE7920D6DE610006E120 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCE0BE8920D6E2B80006E120 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + BCE0BE7B20D6DE610006E120 /* GPUImage_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCE0BE8420D6DE610006E120 /* Build configuration list for PBXNativeTarget "GPUImage_iOS" */; + buildPhases = ( + BCE0BE7720D6DE610006E120 /* Sources */, + BCE0BE7820D6DE610006E120 /* Frameworks */, + BCE0BE7920D6DE610006E120 /* Headers */, + BCE0BE7A20D6DE610006E120 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GPUImage_iOS; + productName = GPUImage; + productReference = BCE0BE7C20D6DE610006E120 /* GPUImage.framework */; + productType = "com.apple.product-type.framework"; + }; + BCE0BE8B20D6E2B80006E120 /* GPUImage_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCE0BE9120D6E2B80006E120 /* Build configuration list for PBXNativeTarget "GPUImage_macOS" */; + buildPhases = ( + BCE0BE8720D6E2B80006E120 /* Sources */, + BCE0BE8820D6E2B80006E120 /* Frameworks */, + BCE0BE8920D6E2B80006E120 /* Headers */, + BCE0BE8A20D6E2B80006E120 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GPUImage_macOS; + productName = GPUImage; + productReference = BCE0BE8C20D6E2B80006E120 /* GPUImage.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BCE0BE7320D6DE610006E120 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "Red Queen Coder, LLC"; + TargetAttributes = { + BCE0BE7B20D6DE610006E120 = { + CreatedOnToolsVersion = 9.3; + LastSwiftMigration = 0930; + }; + BCE0BE8B20D6E2B80006E120 = { + CreatedOnToolsVersion = 9.3; + LastSwiftMigration = 0930; + }; + }; + }; + buildConfigurationList = BCE0BE7620D6DE610006E120 /* Build configuration list for PBXProject "GPUImage" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = BCE0BE7220D6DE610006E120; + productRefGroup = BCE0BE7D20D6DE610006E120 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BCE0BE7B20D6DE610006E120 /* GPUImage_iOS */, + BCE0BE8B20D6E2B80006E120 /* GPUImage_macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BCE0BE7A20D6DE610006E120 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCE0BE8A20D6E2B80006E120 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BCE0BE7720D6DE610006E120 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCE0BEA020D6E3C80006E120 /* Size.swift in Sources */, + BCE0BEA620D6E3C80006E120 /* Matrix.swift in Sources */, + BCE0BEA820D6E3C80006E120 /* ImageOrientation.swift in Sources */, + BCE0BE9E20D6E3C80006E120 /* Color.swift in Sources */, + BCE0BEA220D6E3C80006E120 /* Position.swift in Sources */, + BCE0BEA420D6E3C80006E120 /* Pipeline.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCE0BE8720D6E2B80006E120 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCE0BEA120D6E3C80006E120 /* Size.swift in Sources */, + BCE0BEA720D6E3C80006E120 /* Matrix.swift in Sources */, + BCE0BEA920D6E3C80006E120 /* ImageOrientation.swift in Sources */, + BCE0BE9F20D6E3C80006E120 /* Color.swift in Sources */, + BCE0BEA320D6E3C80006E120 /* Position.swift in Sources */, + BCE0BEA520D6E3C80006E120 /* Pipeline.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + BCE0BE8220D6DE610006E120 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BCE0BE8320D6DE610006E120 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BCE0BE8520D6DE610006E120 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/GPUImage.xcodeproj/GPUImage_Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.GPUImage; + PRODUCT_NAME = GPUImage; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BCE0BE8620D6DE610006E120 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/GPUImage.xcodeproj/GPUImage_Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.GPUImage; + PRODUCT_NAME = GPUImage; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BCE0BE9220D6E2B80006E120 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "$(SRCROOT)/GPUImage.xcodeproj/GPUImage_Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.GPUImage; + PRODUCT_NAME = GPUImage; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + BCE0BE9320D6E2B80006E120 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "$(SRCROOT)/GPUImage.xcodeproj/GPUImage_Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.GPUImage; + PRODUCT_NAME = GPUImage; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BCE0BE7620D6DE610006E120 /* Build configuration list for PBXProject "GPUImage" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCE0BE8220D6DE610006E120 /* Debug */, + BCE0BE8320D6DE610006E120 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCE0BE8420D6DE610006E120 /* Build configuration list for PBXNativeTarget "GPUImage_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCE0BE8520D6DE610006E120 /* Debug */, + BCE0BE8620D6DE610006E120 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCE0BE9120D6E2B80006E120 /* Build configuration list for PBXNativeTarget "GPUImage_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCE0BE9220D6E2B80006E120 /* Debug */, + BCE0BE9320D6E2B80006E120 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BCE0BE7320D6DE610006E120 /* Project object */; +} diff --git a/framework/Source/Color.swift b/framework/Source/Color.swift new file mode 100644 index 0000000..370e4f0 --- /dev/null +++ b/framework/Source/Color.swift @@ -0,0 +1,20 @@ +public struct Color { + public let redComponent:Float + public let greenComponent:Float + public let blueComponent:Float + public let alphaComponent:Float + + public init(red:Float, green:Float, blue:Float, alpha:Float = 1.0) { + self.redComponent = red + self.greenComponent = green + self.blueComponent = blue + self.alphaComponent = alpha + } + + public static let black = Color(red:0.0, green:0.0, blue:0.0, alpha:1.0) + public static let white = Color(red:1.0, green:1.0, blue:1.0, alpha:1.0) + public static let red = Color(red:1.0, green:0.0, blue:0.0, alpha:1.0) + public static let green = Color(red:0.0, green:1.0, blue:0.0, alpha:1.0) + public static let blue = Color(red:0.0, green:0.0, blue:1.0, alpha:1.0) + public static let transparent = Color(red:0.0, green:0.0, blue:0.0, alpha:0.0) +} diff --git a/framework/Source/ImageOrientation.swift b/framework/Source/ImageOrientation.swift new file mode 100644 index 0000000..5901370 --- /dev/null +++ b/framework/Source/ImageOrientation.swift @@ -0,0 +1,42 @@ +public enum ImageOrientation { + case portrait + case portraitUpsideDown + case landscapeLeft + case landscapeRight + + func rotationNeededForOrientation(_ targetOrientation:ImageOrientation) -> Rotation { + switch (self, targetOrientation) { + case (.portrait, .portrait), (.portraitUpsideDown, .portraitUpsideDown), (.landscapeLeft, .landscapeLeft), (.landscapeRight, .landscapeRight): return .noRotation + case (.portrait, .portraitUpsideDown): return .rotate180 + case (.portraitUpsideDown, .portrait): return .rotate180 + case (.portrait, .landscapeLeft): return .rotateCounterclockwise + case (.landscapeLeft, .portrait): return .rotateClockwise + case (.portrait, .landscapeRight): return .rotateClockwise + case (.landscapeRight, .portrait): return .rotateCounterclockwise + case (.landscapeLeft, .landscapeRight): return .rotate180 + case (.landscapeRight, .landscapeLeft): return .rotate180 + case (.portraitUpsideDown, .landscapeLeft): return .rotateClockwise + case (.landscapeLeft, .portraitUpsideDown): return .rotateCounterclockwise + case (.portraitUpsideDown, .landscapeRight): return .rotateCounterclockwise + case (.landscapeRight, .portraitUpsideDown): return .rotateClockwise + } + } +} + +public enum Rotation { + case noRotation + case rotateCounterclockwise + case rotateClockwise + case rotate180 + case flipHorizontally + case flipVertically + case rotateClockwiseAndFlipVertically + case rotateClockwiseAndFlipHorizontally + + func flipsDimensions() -> Bool { + switch self { + case .noRotation, .rotate180, .flipHorizontally, .flipVertically: return false + case .rotateCounterclockwise, .rotateClockwise, .rotateClockwiseAndFlipVertically, .rotateClockwiseAndFlipHorizontally: return true + } + } +} diff --git a/framework/Source/Matrix.swift b/framework/Source/Matrix.swift new file mode 100644 index 0000000..7bbfa42 --- /dev/null +++ b/framework/Source/Matrix.swift @@ -0,0 +1,120 @@ +import QuartzCore + +public struct Matrix4x4 { + public let m11:Float, m12:Float, m13:Float, m14:Float + public let m21:Float, m22:Float, m23:Float, m24:Float + public let m31:Float, m32:Float, m33:Float, m34:Float + public let m41:Float, m42:Float, m43:Float, m44:Float + + public init(rowMajorValues:[Float]) { + guard rowMajorValues.count > 15 else { fatalError("Tried to initialize a 4x4 matrix with fewer than 16 values") } + + self.m11 = rowMajorValues[0] + self.m12 = rowMajorValues[1] + self.m13 = rowMajorValues[2] + self.m14 = rowMajorValues[3] + + self.m21 = rowMajorValues[4] + self.m22 = rowMajorValues[5] + self.m23 = rowMajorValues[6] + self.m24 = rowMajorValues[7] + + self.m31 = rowMajorValues[8] + self.m32 = rowMajorValues[9] + self.m33 = rowMajorValues[10] + self.m34 = rowMajorValues[11] + + self.m41 = rowMajorValues[12] + self.m42 = rowMajorValues[13] + self.m43 = rowMajorValues[14] + self.m44 = rowMajorValues[15] + } + + public static let identity = Matrix4x4(rowMajorValues:[1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0]) +} + +public struct Matrix3x3 { + public let m11:Float, m12:Float, m13:Float + public let m21:Float, m22:Float, m23:Float + public let m31:Float, m32:Float, m33:Float + + public init(rowMajorValues:[Float]) { + guard rowMajorValues.count > 8 else { fatalError("Tried to initialize a 3x3 matrix with fewer than 9 values") } + + self.m11 = rowMajorValues[0] + self.m12 = rowMajorValues[1] + self.m13 = rowMajorValues[2] + + self.m21 = rowMajorValues[3] + self.m22 = rowMajorValues[4] + self.m23 = rowMajorValues[5] + + self.m31 = rowMajorValues[6] + self.m32 = rowMajorValues[7] + self.m33 = rowMajorValues[8] + } + + public static let identity = Matrix3x3(rowMajorValues:[1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0]) + + public static let centerOnly = Matrix3x3(rowMajorValues:[0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0]) +} + +func orthographicMatrix(_ left:Float, right:Float, bottom:Float, top:Float, near:Float, far:Float, anchorTopLeft:Bool = false) -> Matrix4x4 { + let r_l = right - left + let t_b = top - bottom + let f_n = far - near + var tx = -(right + left) / (right - left) + var ty = -(top + bottom) / (top - bottom) + let tz = -(far + near) / (far - near) + + let scale:Float + if (anchorTopLeft) { + scale = 4.0 + tx = -1.0 + ty = -1.0 + } else { + scale = 2.0 + } + + return Matrix4x4(rowMajorValues:[ + scale / r_l, 0.0, 0.0, tx, + 0.0, scale / t_b, 0.0, ty, + 0.0, 0.0, scale / f_n, tz, + 0.0, 0.0, 0.0, 1.0]) +} + + +public extension Matrix4x4 { + public init (_ transform3D:CATransform3D) { + self.m11 = Float(transform3D.m11) + self.m12 = Float(transform3D.m12) + self.m13 = Float(transform3D.m13) + self.m14 = Float(transform3D.m14) + + self.m21 = Float(transform3D.m21) + self.m22 = Float(transform3D.m22) + self.m23 = Float(transform3D.m23) + self.m24 = Float(transform3D.m24) + + self.m31 = Float(transform3D.m31) + self.m32 = Float(transform3D.m32) + self.m33 = Float(transform3D.m33) + self.m34 = Float(transform3D.m34) + + self.m41 = Float(transform3D.m41) + self.m42 = Float(transform3D.m42) + self.m43 = Float(transform3D.m43) + self.m44 = Float(transform3D.m44) + } + + public init (_ transform:CGAffineTransform) { + self.init(CATransform3DMakeAffineTransform(transform)) + } +} diff --git a/framework/Source/Pipeline.swift b/framework/Source/Pipeline.swift new file mode 100755 index 0000000..2100e68 --- /dev/null +++ b/framework/Source/Pipeline.swift @@ -0,0 +1,201 @@ +// MARK: - +// MARK: Basic types +import Foundation + +public protocol ImageSource { + var targets:TargetContainer { get } + func transmitPreviousImage(to target:ImageConsumer, atIndex:UInt) +} + +public protocol ImageConsumer:AnyObject { + var maximumInputs:UInt { get } + var sources:SourceContainer { get } + +// func newFramebufferAvailable(_ framebuffer:Framebuffer, fromSourceIndex:UInt) +} + +public protocol ImageProcessingOperation: ImageConsumer, ImageSource { +} + +infix operator --> : AdditionPrecedence +//precedencegroup ProcessingOperationPrecedence { +// associativity: left +//// higherThan: Multiplicative +//} +@discardableResult public func -->(source:ImageSource, destination:T) -> T { + source.addTarget(destination) + return destination +} + +// MARK: - +// MARK: Extensions and supporting types + +public extension ImageSource { + public func addTarget(_ target:ImageConsumer, atTargetIndex:UInt? = nil) { + if let targetIndex = atTargetIndex { + target.setSource(self, atIndex:targetIndex) + targets.append(target, indexAtTarget:targetIndex) + transmitPreviousImage(to:target, atIndex:targetIndex) + } else if let indexAtTarget = target.addSource(self) { + targets.append(target, indexAtTarget:indexAtTarget) + transmitPreviousImage(to:target, atIndex:indexAtTarget) + } else { + debugPrint("Warning: tried to add target beyond target's input capacity") + } + } + + public func removeAllTargets() { + for (target, index) in targets { + target.removeSourceAtIndex(index) + } + targets.removeAll() + } + +// public func updateTargetsWithFramebuffer(_ framebuffer:Framebuffer) { +// if targets.count == 0 { // Deal with the case where no targets are attached by immediately returning framebuffer to cache +// framebuffer.lock() +// framebuffer.unlock() +// } else { +// // Lock first for each output, to guarantee proper ordering on multi-output operations +// for _ in targets { +// framebuffer.lock() +// } +// } +// for (target, index) in targets { +// target.newFramebufferAvailable(framebuffer, fromSourceIndex:index) +// } +// } +} + +public extension ImageConsumer { + public func addSource(_ source:ImageSource) -> UInt? { + return sources.append(source, maximumInputs:maximumInputs) + } + + public func setSource(_ source:ImageSource, atIndex:UInt) { + _ = sources.insert(source, atIndex:atIndex, maximumInputs:maximumInputs) + } + + public func removeSourceAtIndex(_ index:UInt) { + sources.removeAtIndex(index) + } +} + +class WeakImageConsumer { + weak var value:ImageConsumer? + let indexAtTarget:UInt + init (value:ImageConsumer, indexAtTarget:UInt) { + self.indexAtTarget = indexAtTarget + self.value = value + } +} + +public class TargetContainer:Sequence { + var targets = [WeakImageConsumer]() + var count:Int { get {return targets.count}} + let dispatchQueue = DispatchQueue(label:"com.sunsetlakesoftware.GPUImage.targetContainerQueue", attributes: []) + + public init() { + } + + public func append(_ target:ImageConsumer, indexAtTarget:UInt) { + // TODO: Don't allow the addition of a target more than once + dispatchQueue.async{ + self.targets.append(WeakImageConsumer(value:target, indexAtTarget:indexAtTarget)) + } + } + + public func makeIterator() -> AnyIterator<(ImageConsumer, UInt)> { + var index = 0 + + return AnyIterator { () -> (ImageConsumer, UInt)? in + return self.dispatchQueue.sync{ + if (index >= self.targets.count) { + return nil + } + + while (self.targets[index].value == nil) { + self.targets.remove(at:index) + if (index >= self.targets.count) { + return nil + } + } + + index += 1 + return (self.targets[index - 1].value!, self.targets[index - 1].indexAtTarget) + } + } + } + + public func removeAll() { + dispatchQueue.async{ + self.targets.removeAll() + } + } +} + +public class SourceContainer { + var sources:[UInt:ImageSource] = [:] + + public init() { + } + + public func append(_ source:ImageSource, maximumInputs:UInt) -> UInt? { + var currentIndex:UInt = 0 + while currentIndex < maximumInputs { + if (sources[currentIndex] == nil) { + sources[currentIndex] = source + return currentIndex + } + currentIndex += 1 + } + + return nil + } + + public func insert(_ source:ImageSource, atIndex:UInt, maximumInputs:UInt) -> UInt { + guard (atIndex < maximumInputs) else { fatalError("ERROR: Attempted to set a source beyond the maximum number of inputs on this operation") } + sources[atIndex] = source + return atIndex + } + + public func removeAtIndex(_ index:UInt) { + sources[index] = nil + } +} + +public class ImageRelay: ImageProcessingOperation { +// public var newImageCallback:((Framebuffer) -> ())? + + public let sources = SourceContainer() + public let targets = TargetContainer() + public let maximumInputs:UInt = 1 + public var preventRelay:Bool = false + + public init() { + } + + public func transmitPreviousImage(to target:ImageConsumer, atIndex:UInt) { + sources.sources[0]?.transmitPreviousImage(to:self, atIndex:0) + } + +// public func newFramebufferAvailable(_ framebuffer:Framebuffer, fromSourceIndex:UInt) { +// if let newImageCallback = newImageCallback { +// newImageCallback(framebuffer) +// } +// if (!preventRelay) { +// relayFramebufferOnward(framebuffer) +// } +// } +// +// public func relayFramebufferOnward(_ framebuffer:Framebuffer) { +// // Need to override to guarantee a removal of the previously applied lock +// for _ in targets { +// framebuffer.lock() +// } +// framebuffer.unlock() +// for (target, index) in targets { +// target.newFramebufferAvailable(framebuffer, fromSourceIndex:index) +// } +// } +} diff --git a/framework/Source/Position.swift b/framework/Source/Position.swift new file mode 100644 index 0000000..1b64078 --- /dev/null +++ b/framework/Source/Position.swift @@ -0,0 +1,28 @@ +import Foundation + +#if os(iOS) +import UIKit +#endif + +public struct Position { + public let x:Float + public let y:Float + public let z:Float? + + public init (_ x:Float, _ y:Float, _ z:Float? = nil) { + self.x = x + self.y = y + self.z = z + } + +#if !os(Linux) + public init(point:CGPoint) { + self.x = Float(point.x) + self.y = Float(point.y) + self.z = nil + } +#endif + + public static let center = Position(0.5, 0.5) + public static let zero = Position(0.0, 0.0) +} diff --git a/framework/Source/Size.swift b/framework/Source/Size.swift new file mode 100644 index 0000000..07604c3 --- /dev/null +++ b/framework/Source/Size.swift @@ -0,0 +1,9 @@ +public struct Size { + public let width:Float + public let height:Float + + public init(width:Float, height:Float) { + self.width = width + self.height = height + } +} \ No newline at end of file diff --git a/framework/Source/iOS/GPUImage.h b/framework/Source/iOS/GPUImage.h new file mode 100644 index 0000000..2ba3377 --- /dev/null +++ b/framework/Source/iOS/GPUImage.h @@ -0,0 +1,19 @@ +// +// GPUImage.h +// GPUImage +// +// Created by Brad Larson on 6/17/2018. +// Copyright © 2018 Red Queen Coder, LLC. All rights reserved. +// + +#import + +//! Project version number for GPUImage. +FOUNDATION_EXPORT double GPUImageVersionNumber; + +//! Project version string for GPUImage. +FOUNDATION_EXPORT const unsigned char GPUImageVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +