8 Commits

Author SHA1 Message Date
斑布 3e298925f6 [MetalVideoProcess]: fix export bug. 2020-10-16 14:34:14 +08:00
RenZhu Macro 363816d6ad Add some new transitions.
Erase, Morph, CircleErase...
2020-08-19 10:59:44 +08:00
RenZhu Macro 9d7f65772b Add more video animations. 2020-08-13 10:02:14 +08:00
RenZhu Macro cef6a6188a update readme. 2020-07-29 18:23:36 +08:00
RenZhu Macro 1515f6761b Add new motions
Move up, move right, move left, zoom in ...
2020-07-29 18:19:03 +08:00
RenZhu Macro 8fee841003 fix some bugs. 2020-07-24 11:05:29 +08:00
RenZhu Macro ee85248b23 fix bugs & add more motions.
one video track can only use one motion
2020-07-24 10:51:42 +08:00
RenZhu Macro c135346125 Add some new video transitions & fix bug. 2020-07-23 10:40:14 +08:00
138 changed files with 7434 additions and 375 deletions
+1
View File
@@ -12,3 +12,4 @@ Examples/SimpleVideoAnimation/SimpleVIdeoAnimation.xcodeproj/project.xcworkspace
Examples/SimpleVideoAnimation/SimpleVIdeoAnimation.xcodeproj/xcuserdata
Examples/SimpleVideoAnimation/SimpleVideoAnimation.xcworkspace/xcuserdata
Examples/SimpleVideoTransitionSwitch/SimpleVideoTransitionSwitch.xcodeproj/xcuserdata
Examples/SimpleVideoEditor/SimpleVideoEditor.xcodeproj/project.xcworkspace/xcuserdata
@@ -367,7 +367,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleFourSquareVideo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -385,7 +385,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleFourSquareVideo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
36E063AD2523E25200CCC13B /* PlaygroundVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E063AC2523E25200CCC13B /* PlaygroundVC.swift */; };
6ADB075A24ADDB0C0010A817 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB075924ADDB0C0010A817 /* AppDelegate.swift */; };
6ADB075C24ADDB0C0010A817 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB075B24ADDB0C0010A817 /* SceneDelegate.swift */; };
6ADB075E24ADDB0C0010A817 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB075D24ADDB0C0010A817 /* ViewController.swift */; };
@@ -51,6 +52,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
36E063AC2523E25200CCC13B /* PlaygroundVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaygroundVC.swift; sourceTree = "<group>"; };
6ADB075624ADDB0C0010A817 /* SimpleRealtimeFilterPlayback.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleRealtimeFilterPlayback.app; sourceTree = BUILT_PRODUCTS_DIR; };
6ADB075924ADDB0C0010A817 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6ADB075B24ADDB0C0010A817 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -100,6 +102,7 @@
6ADB075924ADDB0C0010A817 /* AppDelegate.swift */,
6ADB075B24ADDB0C0010A817 /* SceneDelegate.swift */,
6ADB075D24ADDB0C0010A817 /* ViewController.swift */,
36E063AC2523E25200CCC13B /* PlaygroundVC.swift */,
6ADB075F24ADDB0C0010A817 /* Main.storyboard */,
6ADB07DB24AED8910010A817 /* 853.mp4 */,
6ADB07D924AED8220010A817 /* cute.mp4 */,
@@ -219,6 +222,7 @@
6ADB075E24ADDB0C0010A817 /* ViewController.swift in Sources */,
6ADB075A24ADDB0C0010A817 /* AppDelegate.swift in Sources */,
6ADB075C24ADDB0C0010A817 /* SceneDelegate.swift in Sources */,
36E063AD2523E25200CCC13B /* PlaygroundVC.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -371,7 +375,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleRealtimeFilterPlayback/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -389,7 +393,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleRealtimeFilterPlayback/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -1,12 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="omE-bu-A38">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="ngz-2E-aVN">
<objects>
<navigationController id="omE-bu-A38" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="d7c-cd-KMe">
<rect key="frame" x="0.0" y="44" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="kKn-PK-K0V" kind="relationship" relationship="rootViewController" id="yse-EQ-xX6"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="daM-ut-0wO" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1237" y="-69"/>
</scene>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
@@ -16,94 +34,95 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dc5-Dz-opT" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="44.000000000000028" width="375" height="468.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<rect key="frame" x="0.0" y="88.000000000000028" width="375" height="468.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="Dc5-Dz-opT" secondAttribute="height" multiplier="0.8" id="86r-Og-HwX"/>
</constraints>
</view>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="vYk-6H-dKP">
<rect key="frame" x="8" y="517.66666666666663" width="359" height="31"/>
<rect key="frame" x="8" y="561.66666666666663" width="359" height="31"/>
<connections>
<action selector="progressChanged:" destination="BYZ-38-t0r" eventType="valueChanged" id="fwh-uk-uYl"/>
</connections>
</slider>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K6w-Ud-xUK">
<rect key="frame" x="172.66666666666666" y="552.66666666666663" width="30" height="30"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K6w-Ud-xUK">
<rect key="frame" x="172.66666666666666" y="596.66666666666663" width="30" height="30"/>
<state key="normal" title="Play"/>
<connections>
<action selector="play:" destination="BYZ-38-t0r" eventType="touchUpInside" id="cZ4-2E-l3J"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blur track1:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ieB-aM-Q6C">
<rect key="frame" x="20" y="663.66666666666663" width="108" height="21"/>
<rect key="frame" x="20" y="707.66666666666663" width="107" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Gray video:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Il-25-Tij">
<rect key="frame" x="104" y="709.66666666666663" width="87" height="21"/>
<rect key="frame" x="104" y="753.66666666666663" width="87" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Beauty track1:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9fu-53-ZHp">
<rect key="frame" x="20" y="622.66666666666663" width="109" height="21"/>
<rect key="frame" x="20" y="666.66666666666663" width="109" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="track2:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Y6k-OT-zIp">
<rect key="frame" x="208" y="622.66666666666663" width="54" height="21"/>
<rect key="frame" x="208" y="666.66666666666663" width="54" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="track2:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wCL-xj-Wni">
<rect key="frame" x="207" y="663.66666666666663" width="54" height="21"/>
<rect key="frame" x="206" y="707.66666666666663" width="54" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Lbm-mS-3au">
<rect key="frame" x="139" y="617.66666666666663" width="51" height="31"/>
<rect key="frame" x="139" y="661.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="f2R-IT-cw2"/>
</connections>
</switch>
<switch opaque="NO" tag="1" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="rJO-mU-xYY">
<rect key="frame" x="138" y="658.66666666666663" width="51" height="31"/>
<rect key="frame" x="137" y="702.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="yx1-uC-eEF"/>
</connections>
</switch>
<switch opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="eJu-z5-owd">
<rect key="frame" x="272" y="617.66666666666663" width="51" height="31"/>
<rect key="frame" x="272" y="661.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="9Y9-ZR-JFD"/>
</connections>
</switch>
<switch opaque="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Ufa-eY-5ZC">
<rect key="frame" x="271" y="658.66666666666663" width="51" height="31"/>
<rect key="frame" x="270" y="702.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="Bsk-J9-CjP"/>
</connections>
</switch>
<switch opaque="NO" tag="4" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="tbD-8b-LUS">
<rect key="frame" x="201" y="704.66666666666663" width="51" height="31"/>
<rect key="frame" x="201" y="748.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="J3N-K6-5di"/>
</connections>
</switch>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gMy-fV-lJB">
<rect key="frame" x="166.66666666666666" y="587.66666666666663" width="42" height="30"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gMy-fV-lJB">
<rect key="frame" x="166.66666666666666" y="631.66666666666663" width="42" height="30"/>
<state key="normal" title="Pause"/>
<connections>
<action selector="pause:" destination="BYZ-38-t0r" eventType="touchUpInside" id="WbY-hn-U3f"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="eJu-z5-owd" firstAttribute="leading" secondItem="Y6k-OT-zIp" secondAttribute="trailing" constant="10" id="0Ih-iu-pAo"/>
<constraint firstItem="Y6k-OT-zIp" firstAttribute="centerY" secondItem="Lbm-mS-3au" secondAttribute="centerY" id="5T7-S9-kXH"/>
@@ -136,8 +155,8 @@
<constraint firstItem="9fu-53-ZHp" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="ynQ-x2-1Fe"/>
<constraint firstItem="eJu-z5-owd" firstAttribute="centerY" secondItem="Y6k-OT-zIp" secondAttribute="centerY" id="z7O-GK-or8"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<navigationItem key="navigationItem" id="INN-Cx-VbS"/>
<connections>
<outlet property="progress" destination="vYk-6H-dKP" id="qNJ-VV-aFL"/>
<outlet property="renderView" destination="Dc5-Dz-opT" id="LzR-68-614"/>
@@ -145,7 +164,111 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="131.8840579710145" y="137.94642857142856"/>
<point key="canvasLocation" x="1065" y="411"/>
</scene>
<!--View Controller-->
<scene sceneID="l41-w0-TrY">
<objects>
<viewController id="kKn-PK-K0V" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Bjp-hz-Fn2">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZjZ-Ux-Ghm">
<rect key="frame" x="148.66666666666666" y="391" width="78" height="30"/>
<state key="normal" title="Playground"/>
<connections>
<segue destination="afR-es-Hbs" kind="show" id="A6B-e2-0n4"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FMM-3O-BgN">
<rect key="frame" x="151" y="431" width="73" height="30"/>
<state key="normal" title="MultiVideo"/>
<connections>
<segue destination="BYZ-38-t0r" kind="show" id="WXt-uP-yZo"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="l6C-up-lB1"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="FMM-3O-BgN" firstAttribute="top" secondItem="ZjZ-Ux-Ghm" secondAttribute="bottom" constant="10" id="3qm-yn-BcB"/>
<constraint firstItem="FMM-3O-BgN" firstAttribute="centerX" secondItem="Bjp-hz-Fn2" secondAttribute="centerX" id="8Km-bN-wp1"/>
<constraint firstItem="ZjZ-Ux-Ghm" firstAttribute="centerX" secondItem="Bjp-hz-Fn2" secondAttribute="centerX" id="8MN-4O-p65"/>
<constraint firstItem="ZjZ-Ux-Ghm" firstAttribute="centerY" secondItem="Bjp-hz-Fn2" secondAttribute="centerY" id="b6n-6P-sux"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="UVZ-3x-Uyx"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="yNO-yo-vzK" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-404" y="-69.458128078817737"/>
</scene>
<!--PlaygroundVC-->
<scene sceneID="5qv-Wk-ccO">
<objects>
<viewController id="afR-es-Hbs" customClass="PlaygroundVC" customModule="SimpleRealtimeFilterPlayback" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="2uZ-cD-AQm">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="T1T-WM-jRk" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="88.000000000000028" width="375" height="468.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="T1T-WM-jRk" secondAttribute="height" multiplier="0.8" id="j6X-Xj-fIT"/>
</constraints>
</view>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="m9P-aS-DOe">
<rect key="frame" x="8" y="561.66666666666663" width="359" height="31"/>
<connections>
<action selector="progressChanged:" destination="afR-es-Hbs" eventType="valueChanged" id="fgH-WE-XlN"/>
</connections>
</slider>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="j6P-6j-YVr">
<rect key="frame" x="172.66666666666666" y="596.66666666666663" width="30" height="30"/>
<state key="normal" title="Play"/>
<connections>
<action selector="play:" destination="afR-es-Hbs" eventType="touchUpInside" id="ruz-mE-tpy"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vAH-WV-nnK">
<rect key="frame" x="166.66666666666666" y="631.66666666666663" width="42" height="30"/>
<state key="normal" title="Pause"/>
<connections>
<action selector="pause:" destination="afR-es-Hbs" eventType="touchUpInside" id="IsH-CI-uaA"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="law-Lw-xq9"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="T1T-WM-jRk" firstAttribute="leading" secondItem="law-Lw-xq9" secondAttribute="leading" id="0g4-SY-2dL"/>
<constraint firstItem="j6P-6j-YVr" firstAttribute="top" secondItem="m9P-aS-DOe" secondAttribute="bottom" constant="5" id="50E-VL-Gdw"/>
<constraint firstItem="T1T-WM-jRk" firstAttribute="top" secondItem="law-Lw-xq9" secondAttribute="top" id="5rf-K8-jDy"/>
<constraint firstItem="vAH-WV-nnK" firstAttribute="centerX" secondItem="2uZ-cD-AQm" secondAttribute="centerX" id="5uI-W3-4Qf"/>
<constraint firstItem="m9P-aS-DOe" firstAttribute="leading" secondItem="law-Lw-xq9" secondAttribute="leading" constant="10" id="999-hW-XHL"/>
<constraint firstItem="law-Lw-xq9" firstAttribute="trailing" secondItem="m9P-aS-DOe" secondAttribute="trailing" constant="10" id="Gr4-Q1-U2S"/>
<constraint firstItem="law-Lw-xq9" firstAttribute="trailing" secondItem="T1T-WM-jRk" secondAttribute="trailing" id="IWm-ud-ETw"/>
<constraint firstItem="vAH-WV-nnK" firstAttribute="top" secondItem="j6P-6j-YVr" secondAttribute="bottom" constant="5" id="LQP-Bv-GJ9"/>
<constraint firstItem="m9P-aS-DOe" firstAttribute="top" secondItem="T1T-WM-jRk" secondAttribute="bottom" constant="5" id="S4w-RT-SbR"/>
<constraint firstItem="j6P-6j-YVr" firstAttribute="centerX" secondItem="2uZ-cD-AQm" secondAttribute="centerX" id="XHf-mP-3mo"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="Aqz-co-Lc4"/>
<connections>
<outlet property="progress" destination="m9P-aS-DOe" id="Jjh-H5-Qsf"/>
<outlet property="renderView" destination="T1T-WM-jRk" id="QEN-az-weS"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6bn-ay-7JA" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1066" y="-263"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -20,6 +20,8 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Use</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
@@ -0,0 +1,91 @@
//
// PlaygroundVC.swift
// SimpleRealtimeFilterPlayback
//
// Created by wangrenzhu on 2020/9/30.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import UIKit
import AVFoundation
import MetalVideoProcess
class PlaygroundVC: UIViewController {
@IBOutlet weak var renderView: MetalVideoProcessRenderView!
@IBOutlet weak var progress: UISlider!
var grayFilter: MetalVideoProcessLuminance?
var player: MetalVideoProcessPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let asset1 = AVAsset(url: Bundle.main.url(forResource: "853", withExtension: "mp4")!)
let item1 = MetalVideoEditorItem(asset: asset1)
do {
let editor = try MetalVideoEditor(videoItems: [item1],
customVideoCompositorClass: MetalVideoProcessCompositor.self)
let playerItem = editor.buildPlayerItem()
self.progress.maximumValue = Float(playerItem.duration.seconds)
let player = try MetalVideoProcessPlayer(playerItem: playerItem)
let gray = MetalVideoProcessLuminance()
gray.saveUniformSettings(forTimelineRange: CMTimeRange(start: CMTime(seconds: 0.5), duration: CMTime(seconds: 3.0)), trackID: item1.trackID)
gray.isEnable = true
self.grayFilter = gray
player.addTarget(gray, atTargetIndex: nil, trackID: item1.trackID, targetTrackId: 0)
gray --> renderView
player.playerDelegate = self
self.player = player
} catch {
}
}
override func viewDidDisappear(_ animated: Bool) {
self.player?.suspend()
self.player?.dispose()
}
@IBAction func play(_ sender: Any) {
self.player?.play()
}
@IBAction func pause(_ sender: Any) {
self.player?.pause()
}
@IBAction func progressChanged(_ sender: UISlider) {
let value = sender.value
self.player?.seekTo(time: Float64(value))
}
}
extension PlaygroundVC: MetalVideoProcessPlayerDelegate {
func playbackFrameTimeChanged(frameTime time: CMTime, player: AVPlayer) {
DispatchQueue.main.async {
self.progress.value = Float(time.seconds)
}
}
func playEnded(currentPlayer player: AVPlayer) {
}
func finishExport(error: NSError?) {
}
func exportProgressChanged(_ progress: Float) {
}
}
@@ -18,6 +18,7 @@
6A874BB924C5462C00ADFD8A /* MetalVideoProcess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A874BB824C545CC00ADFD8A /* MetalVideoProcess.framework */; };
6A874BBA24C5462C00ADFD8A /* MetalVideoProcess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6A874BB824C545CC00ADFD8A /* MetalVideoProcess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6A874BDA24C6FDE200ADFD8A /* 853.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 6A874BD824C6FDE200ADFD8A /* 853.mp4 */; };
AB59B95124C9717600943BE2 /* NavigateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94F24C9717600943BE2 /* NavigateViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -58,6 +59,7 @@
6A874BB324C545CC00ADFD8A /* MetalVideoProcess.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MetalVideoProcess.xcodeproj; path = ../../MetalVideoProcess.xcodeproj; sourceTree = "<group>"; };
6A874BD824C6FDE200ADFD8A /* 853.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; name = 853.mp4; path = SimpleVIdeoAnimation/853.mp4; sourceTree = SOURCE_ROOT; };
A512CD4DD2F5AB414CF01FE6 /* Pods-SimpleVideoAnimation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleVideoAnimation.release.xcconfig"; path = "Target Support Files/Pods-SimpleVideoAnimation/Pods-SimpleVideoAnimation.release.xcconfig"; sourceTree = "<group>"; };
AB59B94F24C9717600943BE2 /* NavigateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NavigateViewController.swift; path = SimpleVIdeoAnimation/NavigateViewController.swift; sourceTree = SOURCE_ROOT; };
D107CF0887B69DDEA376CBEA /* Pods-SimpleVideoAnimation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleVideoAnimation.debug.xcconfig"; path = "Target Support Files/Pods-SimpleVideoAnimation/Pods-SimpleVideoAnimation.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -109,6 +111,7 @@
6A874B9F24C53EF700ADFD8A /* AppDelegate.swift */,
6A874BA124C53EF700ADFD8A /* SceneDelegate.swift */,
6A874BA324C53EF700ADFD8A /* ViewController.swift */,
AB59B94F24C9717600943BE2 /* NavigateViewController.swift */,
6A874BA524C53EF700ADFD8A /* Main.storyboard */,
6A874BA824C53EF800ADFD8A /* Assets.xcassets */,
6A874BAA24C53EF800ADFD8A /* LaunchScreen.storyboard */,
@@ -269,6 +272,7 @@
buildActionMask = 2147483647;
files = (
6A874BA424C53EF700ADFD8A /* ViewController.swift in Sources */,
AB59B95124C9717600943BE2 /* NavigateViewController.swift in Sources */,
6A874BA024C53EF700ADFD8A /* AppDelegate.swift in Sources */,
6A874BA224C53EF700ADFD8A /* SceneDelegate.swift in Sources */,
);
@@ -1,50 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Sjl-30-bHq">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SimpleVideoAnimation" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="ViewController" id="BYZ-38-t0r" customClass="ViewController" customModule="SimpleVideoAnimation" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FXj-LY-59h" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="0.0" width="375" height="375"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FXj-LY-59h" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="0.0" width="375" height="438"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="FXj-LY-59h" secondAttribute="height" id="iBP-Gu-Wj9"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gLe-Zn-niL">
<rect key="frame" x="166.5" y="515" width="42" height="30"/>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gLe-Zn-niL">
<rect key="frame" x="231" y="562" width="42" height="30"/>
<state key="normal" title="Pause"/>
<connections>
<action selector="pause:" destination="BYZ-38-t0r" eventType="touchUpInside" id="jXA-Ko-2E5"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9lP-KN-D4J">
<rect key="frame" x="172.5" y="475" width="30" height="30"/>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9lP-KN-D4J">
<rect key="frame" x="136" y="562" width="30" height="30"/>
<state key="normal" title="Play"/>
<connections>
<action selector="play:" destination="BYZ-38-t0r" eventType="touchUpInside" id="BxN-gS-f9o"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JkB-hg-7A6" customClass="TrimmerView" customModule="PryntTrimmerView">
<rect key="frame" x="0.0" y="385" width="375" height="80"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JkB-hg-7A6" customClass="TrimmerView" customModule="PryntTrimmerView">
<rect key="frame" x="0.0" y="470" width="375" height="80"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="9zr-ar-gfx"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="9lP-KN-D4J" firstAttribute="top" secondItem="JkB-hg-7A6" secondAttribute="bottom" constant="10" id="EVm-H0-xCG"/>
<constraint firstItem="FXj-LY-59h" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="Hbb-9e-X3i"/>
@@ -57,7 +60,6 @@
<constraint firstItem="JkB-hg-7A6" firstAttribute="top" secondItem="FXj-LY-59h" secondAttribute="bottom" constant="10" id="rCV-zQ-Eyi"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="FXj-LY-59h" secondAttribute="trailing" id="rUK-5t-TAP"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="progressView" destination="JkB-hg-7A6" id="eky-zy-L0Z"/>
@@ -66,7 +68,27 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="132" y="138"/>
<point key="canvasLocation" x="132" y="137.18140929535232"/>
</scene>
<!--NavigateView-->
<scene sceneID="XBJ-QK-b6X">
<objects>
<viewController id="Sjl-30-bHq" userLabel="NavigateView" customClass="NavigateViewController" customModule="SimpleVideoAnimation" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="aeg-zc-cBS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="qlq-zn-i27"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Aj9-BM-fNL" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-906" y="137"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -0,0 +1,110 @@
//
// NavigateViewController.swift
// SimpleVideoTransition
//
// Created by Ruanshengqiang Macro on 2020/7/2.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
import MetalVideoProcess
import AVFoundation
import MetalVideoProcess
class NavigateViewController: UIViewController {
@IBOutlet weak var naviStackView: UIStackView!
var transitionTimeRange = CMTimeRange.init()
let N = 19
private func getTransition(index: Int) -> (titleName: String, transObject: MetalVideoProcessMotion) {
switch index {
case 0:
return (titleName: "渐显", transObject: MetalVideoProcessFadeInMotion())
case 1:
return (titleName: "轻微放大", transObject: MetalVideoProcessSlimZoomInMotion())
case 2:
return (titleName: "放大", transObject: MetalVideoProcessZoomInMotion())
case 3:
return (titleName: "缩小", transObject: MetalVideoProcessZoomOutMotion())
case 4:
return (titleName: "向左滑入", transObject: MetalVideoProcessMoveLeftMotion())
case 5:
return (titleName: "向右滑入", transObject: MetalVideoProcessMoveRightMotion())
case 6:
return (titleName: "向下滑动", transObject: MetalVideoProcessMoveDownMotion())
case 7:
return (titleName: "向上滑动", transObject: MetalVideoProcessMoveUpMotion())
case 8:
return (titleName: "旋转", transObject: MetalVideoProcessRotateMotion())
case 9:
return (titleName: "涡旋旋转", transObject: MetalVideoProcessSwirlMotion())
case 10:
return (titleName: "镜像旋转", transObject: MetalVideoProcessMirrorRotateMotion())
case 11:
return (titleName: "向上旋入", transObject: MetalVideoProcessUpMoveInBlurMotion())
case 12:
return (titleName: "向上旋入II", transObject: MetalVideoProcessUpMoveInBlurIIMotion())
case 13:
return (titleName: "雨刷", transObject: MetalVideoProcessWiperMotion())
case 14:
return (titleName: "摆钟", transObject: MetalVideoProcessPendulumMotion())
case 15:
return (titleName: "向右甩入", transObject: MetalVideoProcessRightDropMotion())
case 16:
return (titleName: "向下甩入", transObject: MetalVideoProcessUpDropMotion())
case 17:
return (titleName: "动感放大", transObject: MetalVideoProcessZoomInBlurMotion())
case 18:
return (titleName: "动感缩小", transObject: MetalVideoProcessZoomOutBlurMotion())
/*
case 15:
return (titleName: "", transObject: MetalVideoProcessRotateInRightMotion())
*/
default:
return (titleName: "放大", transObject: MetalVideoProcessMoveUpMotion())
}
}
private func getButton(index: Int) -> UIButton {
let splitNum = 5.0
let pieceWidth = Double(self.view.frame.width) / splitNum
let buttonWidth = pieceWidth * 0.8
let dIndex = Double(index)
let indexX = fmod(dIndex, splitNum) * pieceWidth
let indexY = (floor(dIndex / splitNum)) * pieceWidth + 100.0
let buttonFrame = CGRect(x: indexX, y: indexY, width: buttonWidth, height: buttonWidth)
let but = UIButton(frame: buttonFrame)
but.backgroundColor = UIColor(red: 0.3, green: 0.4, blue: 0.3, alpha: 0.7)
but.setTitle(getTransition(index: index).titleName, for: .normal)
but.titleLabel?.adjustsFontSizeToFitWidth = true
but.addTarget(self, action: #selector(self.swithController(_:)), for: .touchUpInside)
but.tag = index
return but
}
override func viewDidLoad() {
super.viewDidLoad()
for index in 0...N - 1 {
self.view.addSubview(getButton(index: index))
}
}
@IBAction func swithController(_ sender: UIButton) {
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
let trans = getTransition(index: sender.tag)
viewController.motionIn = trans.transObject
self.present(viewController, animated: true, completion: nil)
}
}
@@ -13,13 +13,14 @@ import PryntTrimmerView
class ViewController: UIViewController {
@IBOutlet weak var renderView: MetalVideoProcessRenderView!
@IBOutlet weak var progressView: TrimmerView!
var player: MetalVideoProcessPlayer?
var editor: MetalVideoEditor?
public var motionIn: MetalVideoProcessMotion = MetalVideoProcessMoveUpMotion()
override func viewDidLoad() {
super.viewDidLoad()
@@ -32,13 +33,13 @@ class ViewController: UIViewController {
self.editor = editor
let playerItem = editor.buildPlayerItem()
let fadeIn = MetalVideoProcessFadeInMotion()
let fadeIn = motionIn
fadeIn.timingType = .quadraticEaseOut
let fadeOut = MetalVideoProcessFadeOutMotion()
fadeOut.timingType = .quarticEaseOut
let fadeInTimeRange = CMTimeRangeMake(start: CMTime(seconds: 2.0), duration: CMTime(seconds: 2.0))
let fadeInTimeRange = CMTimeRangeMake(start: CMTime(seconds: 0.0), duration: CMTime(seconds: 2.0))
fadeIn.saveUniformSettings(forTimelineRange: fadeInTimeRange, trackID: item1.trackID)
let fadeOutTimeRange = CMTimeRange(start: item1.timeRange.end - CMTime(seconds: 4.0), duration: CMTime(seconds: 1.5))
@@ -54,17 +55,14 @@ class ViewController: UIViewController {
let transform = MetalVideoProcessTransformFilter()
transform.saveUniformSettings(forTimelineRange: item1.timeRange, trackID: item1.trackID)
transform.roi = CGRect(x: 0.25, y: 0.25, width: 0.5, height: 0.5)
transform.roi = CGRect(x: 0.5, y: 0.5, width: 0.5, height: 0.5)
fadeIn.roi = transform.roi
player.addTarget(background, atTargetIndex: 0, trackID: item1.trackID, targetTrackId: 0)
background --> fadeIn //source 0
player.addTarget(transform, atTargetIndex: nil, trackID: item1.trackID, targetTrackId: 0) //source 1
transform --> fadeIn //source 1
background --> fadeOut //source 0
fadeIn --> fadeOut --> renderView //source 1
transform --> fadeIn --> renderView //source 1
self.player = player
} catch {
@@ -82,6 +80,11 @@ class ViewController: UIViewController {
self.progressView.delegate = self
}
override func viewDidDisappear(_ animated: Bool) {
self.player?.suspend()
self.player?.dispose()
}
@IBAction func play(_ sender: Any) {
self.player?.play()
}
@@ -572,7 +572,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoEditor/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -591,7 +591,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoEditor/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -136,8 +136,9 @@ extension ViewController: MetalVideoProcessPlayerDelegate {
source --> self.movieWriter!
self.view.isUserInteractionEnabled = false
try? self.player?.startExport()
self.movieWriter?.activateAudioTrack()
self.movieWriter?.startRecording()
try? self.player?.startExport()
}
}
@@ -58,7 +58,7 @@ extension ViewController: UIGestureRecognizerDelegate {
let tanl = ges.translation(in: ges.view)
// have to content the view scaleFactor and the vertex to texture scale.
transformFilter.translation = Position(currentPostion.x + Float(tanl.x * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.width), currentPostion.y - Float(tanl.y * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.height))
transformFilter.translation = Position(currentPostion.x + Float(tanl.x * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.width), 1.0 - currentPostion.y - Float(tanl.y * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.height))
} else if ges.state == .ended {
currentPostion = (transformFilter.translation)
@@ -385,7 +385,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoExport/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -405,7 +405,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoExport/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -118,9 +118,10 @@ class ViewController: UIViewController {
source --> self.movieWriter!
self.view.isUserInteractionEnabled = false
try? self.player?.startExport()
self.movieWriter?.activateAudioTrack()
self.movieWriter?.startRecording()
try? self.player?.startExport()
}
@@ -17,6 +17,7 @@
6ADB07A924ADDE450010A817 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6ADB07A724ADDE450010A817 /* Main.storyboard */; };
6ADB07AB24ADDE460010A817 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6ADB07AA24ADDE460010A817 /* Assets.xcassets */; };
6ADB07AE24ADDE460010A817 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6ADB07AC24ADDE460010A817 /* LaunchScreen.storyboard */; };
AB59B8CC24C5AC8000943BE2 /* NaviViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B8CA24C5AC8000943BE2 /* NaviViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -55,6 +56,7 @@
6ADB07AA24ADDE460010A817 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6ADB07AD24ADDE460010A817 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
6ADB07AF24ADDE460010A817 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AB59B8CA24C5AC8000943BE2 /* NaviViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NaviViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -107,6 +109,7 @@
children = (
6A3E708C24C067C3009A0AC9 /* 853.mp4 */,
6A3E708E24C067C3009A0AC9 /* cute.mp4 */,
AB59B8CA24C5AC8000943BE2 /* NaviViewController.swift */,
6ADB07A124ADDE450010A817 /* AppDelegate.swift */,
6ADB07A324ADDE450010A817 /* SceneDelegate.swift */,
6ADB07A524ADDE450010A817 /* ViewController.swift */,
@@ -146,7 +149,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1150;
LastUpgradeCheck = 1150;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "RenZhu Macro";
TargetAttributes = {
6ADB079D24ADDE450010A817 = {
@@ -208,6 +211,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AB59B8CC24C5AC8000943BE2 /* NaviViewController.swift in Sources */,
6ADB07A624ADDE450010A817 /* ViewController.swift in Sources */,
6ADB07A224ADDE450010A817 /* AppDelegate.swift in Sources */,
6ADB07A424ADDE450010A817 /* SceneDelegate.swift in Sources */,
@@ -262,6 +266,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -322,6 +327,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -355,7 +361,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoTransition/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -374,7 +380,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = 3BJYKWHMM4;
INFOPLIST_FILE = SimpleVideoTransition/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6ADB079D24ADDE450010A817"
BuildableName = "SimpleVideoTransition.app"
BlueprintName = "SimpleVideoTransition"
ReferencedContainer = "container:SimpleVideoTransition.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6ADB079D24ADDE450010A817"
BuildableName = "SimpleVideoTransition.app"
BlueprintName = "SimpleVideoTransition"
ReferencedContainer = "container:SimpleVideoTransition.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6ADB079D24ADDE450010A817"
BuildableName = "SimpleVideoTransition.app"
BlueprintName = "SimpleVideoTransition"
ReferencedContainer = "container:SimpleVideoTransition.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,142 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="AUR-96-71u">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SimpleVideoTransition" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="ViewController" id="BYZ-38-t0r" customClass="ViewController" customModule="SimpleVideoTransition" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dc5-Dz-opT" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="44.000000000000028" width="375" height="468.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="Dc5-Dz-opT" secondAttribute="height" multiplier="0.8" id="86r-Og-HwX"/>
</constraints>
</view>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="vYk-6H-dKP">
<rect key="frame" x="8" y="517.66666666666663" width="359" height="31"/>
<slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="vYk-6H-dKP">
<rect key="frame" x="2" y="576" width="359" height="31"/>
<connections>
<action selector="progressChanged:" destination="BYZ-38-t0r" eventType="valueChanged" id="fwh-uk-uYl"/>
</connections>
</slider>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K6w-Ud-xUK">
<rect key="frame" x="172.66666666666666" y="552.66666666666663" width="30" height="30"/>
<state key="normal" title="Play"/>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Dc5-Dz-opT" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="0.0" y="100" width="375" height="468.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="Dc5-Dz-opT" secondAttribute="height" multiplier="0.8" id="86r-Og-HwX"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8dD-NO-sG4">
<rect key="frame" x="192" y="612" width="69" height="42"/>
<constraints>
<constraint firstAttribute="height" constant="42" id="M3N-FO-erk"/>
</constraints>
<state key="normal" title="pause"/>
<connections>
<action selector="play:" destination="BYZ-38-t0r" eventType="touchUpInside" id="cZ4-2E-l3J"/>
<action selector="pause:" destination="BYZ-38-t0r" eventType="touchUpInside" id="koL-AD-Vdy"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blur track1:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ieB-aM-Q6C">
<rect key="frame" x="20" y="663.66666666666663" width="108" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Gray video:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Il-25-Tij">
<rect key="frame" x="104" y="709.66666666666663" width="87" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Beauty track1:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9fu-53-ZHp">
<rect key="frame" x="20" y="622.66666666666663" width="109" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="track2:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Y6k-OT-zIp">
<rect key="frame" x="208" y="622.66666666666663" width="54" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="track2:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wCL-xj-Wni">
<rect key="frame" x="207" y="663.66666666666663" width="54" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Lbm-mS-3au">
<rect key="frame" x="139" y="617.66666666666663" width="51" height="31"/>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L7o-SP-KhR">
<rect key="frame" x="104" y="612" width="69" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="5g0-CY-YQ2"/>
</constraints>
<state key="normal" title="play"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="f2R-IT-cw2"/>
</connections>
</switch>
<switch opaque="NO" tag="1" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="rJO-mU-xYY">
<rect key="frame" x="138" y="658.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="yx1-uC-eEF"/>
</connections>
</switch>
<switch opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="eJu-z5-owd">
<rect key="frame" x="272" y="617.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="9Y9-ZR-JFD"/>
</connections>
</switch>
<switch opaque="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Ufa-eY-5ZC">
<rect key="frame" x="271" y="658.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="Bsk-J9-CjP"/>
</connections>
</switch>
<switch opaque="NO" tag="4" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="tbD-8b-LUS">
<rect key="frame" x="201" y="704.66666666666663" width="51" height="31"/>
<connections>
<action selector="filterOn:" destination="BYZ-38-t0r" eventType="valueChanged" id="J3N-K6-5di"/>
</connections>
</switch>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gMy-fV-lJB">
<rect key="frame" x="166.66666666666666" y="587.66666666666663" width="42" height="30"/>
<state key="normal" title="Pause"/>
<connections>
<action selector="pause:" destination="BYZ-38-t0r" eventType="touchUpInside" id="WbY-hn-U3f"/>
<action selector="play:" destination="BYZ-38-t0r" eventType="touchUpInside" id="ec4-6v-Fe0"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="eJu-z5-owd" firstAttribute="leading" secondItem="Y6k-OT-zIp" secondAttribute="trailing" constant="10" id="0Ih-iu-pAo"/>
<constraint firstItem="Y6k-OT-zIp" firstAttribute="centerY" secondItem="Lbm-mS-3au" secondAttribute="centerY" id="5T7-S9-kXH"/>
<constraint firstItem="wCL-xj-Wni" firstAttribute="leading" secondItem="rJO-mU-xYY" secondAttribute="trailing" constant="20" id="76t-qu-CCa"/>
<constraint firstItem="Dc5-Dz-opT" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="7MR-S1-7Xq"/>
<constraint firstItem="Lbm-mS-3au" firstAttribute="leading" secondItem="9fu-53-ZHp" secondAttribute="trailing" constant="10" id="8Uu-ol-v3J"/>
<constraint firstItem="vYk-6H-dKP" firstAttribute="top" secondItem="Dc5-Dz-opT" secondAttribute="bottom" constant="5" id="9FO-ag-qBs"/>
<constraint firstItem="Dc5-Dz-opT" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="BlF-9Y-vIn"/>
<constraint firstItem="gMy-fV-lJB" firstAttribute="top" secondItem="K6w-Ud-xUK" secondAttribute="bottom" constant="5" id="Gi5-pV-rO9"/>
<constraint firstItem="gMy-fV-lJB" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Gqx-bc-4zc"/>
<constraint firstItem="Ufa-eY-5ZC" firstAttribute="centerY" secondItem="wCL-xj-Wni" secondAttribute="centerY" id="Hgc-Ac-M81"/>
<constraint firstItem="rJO-mU-xYY" firstAttribute="leading" secondItem="ieB-aM-Q6C" secondAttribute="trailing" constant="10" id="Iwt-Iv-Se2"/>
<constraint firstItem="K6w-Ud-xUK" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="LaJ-so-qEc"/>
<constraint firstItem="8Il-25-Tij" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" constant="-40" id="OpR-uB-WtM"/>
<constraint firstItem="L7o-SP-KhR" firstAttribute="top" secondItem="vYk-6H-dKP" secondAttribute="bottom" constant="6" id="FQa-7W-Nj8"/>
<constraint firstItem="8dD-NO-sG4" firstAttribute="leading" secondItem="L7o-SP-KhR" secondAttribute="trailing" constant="19" id="Fjg-ZY-9uz"/>
<constraint firstItem="L7o-SP-KhR" firstAttribute="top" secondItem="8dD-NO-sG4" secondAttribute="top" id="INb-AW-t9N"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="L7o-SP-KhR" secondAttribute="bottom" constant="113" id="N7W-JJ-rFn"/>
<constraint firstItem="L7o-SP-KhR" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="104" id="P4W-wv-UMV"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Dc5-Dz-opT" secondAttribute="trailing" id="Rt7-yT-k9J"/>
<constraint firstItem="vYk-6H-dKP" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="10" id="UKf-H0-pvm"/>
<constraint firstItem="tbD-8b-LUS" firstAttribute="centerY" secondItem="8Il-25-Tij" secondAttribute="centerY" id="Vqa-LB-raz"/>
<constraint firstItem="8Il-25-Tij" firstAttribute="top" secondItem="rJO-mU-xYY" secondAttribute="bottom" constant="20" id="WcG-FP-5ve"/>
<constraint firstItem="wCL-xj-Wni" firstAttribute="centerY" secondItem="rJO-mU-xYY" secondAttribute="centerY" id="ZZc-6F-iqE"/>
<constraint firstItem="9fu-53-ZHp" firstAttribute="top" secondItem="gMy-fV-lJB" secondAttribute="bottom" constant="5" id="a0O-Xf-ftz"/>
<constraint firstItem="ieB-aM-Q6C" firstAttribute="top" secondItem="9fu-53-ZHp" secondAttribute="bottom" constant="20" id="aEl-4X-oGJ"/>
<constraint firstItem="K6w-Ud-xUK" firstAttribute="top" secondItem="vYk-6H-dKP" secondAttribute="bottom" constant="5" id="aYl-Sa-qn2"/>
<constraint firstItem="tbD-8b-LUS" firstAttribute="leading" secondItem="8Il-25-Tij" secondAttribute="trailing" constant="10" id="eiC-rb-qZN"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="8dD-NO-sG4" secondAttribute="trailing" constant="114" id="ajW-8P-48Y"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="vYk-6H-dKP" secondAttribute="trailing" constant="10" id="itv-bP-sNm"/>
<constraint firstItem="Y6k-OT-zIp" firstAttribute="leading" secondItem="Lbm-mS-3au" secondAttribute="trailing" constant="20" id="jyW-v3-sUE"/>
<constraint firstItem="ieB-aM-Q6C" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="lU2-BQ-chN"/>
<constraint firstItem="Ufa-eY-5ZC" firstAttribute="leading" secondItem="wCL-xj-Wni" secondAttribute="trailing" constant="10" id="sM2-LG-67H"/>
<constraint firstItem="Lbm-mS-3au" firstAttribute="centerY" secondItem="9fu-53-ZHp" secondAttribute="centerY" id="sef-Y8-TX1"/>
<constraint firstItem="rJO-mU-xYY" firstAttribute="centerY" secondItem="ieB-aM-Q6C" secondAttribute="centerY" id="uRA-u6-Tw6"/>
<constraint firstItem="9fu-53-ZHp" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="ynQ-x2-1Fe"/>
<constraint firstItem="eJu-z5-owd" firstAttribute="centerY" secondItem="Y6k-OT-zIp" secondAttribute="centerY" id="z7O-GK-or8"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="8dD-NO-sG4" secondAttribute="bottom" constant="124" id="mjl-wp-mzn"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="progress" destination="vYk-6H-dKP" id="qNJ-VV-aFL"/>
@@ -145,7 +76,27 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="131.8840579710145" y="137.94642857142856"/>
<point key="canvasLocation" x="130.40000000000001" y="137.4384236453202"/>
</scene>
<!--NaviViewController-->
<scene sceneID="kl0-RY-FE2">
<objects>
<viewController storyboardIdentifier="NaviViewController" id="AUR-96-71u" userLabel="NaviViewController" customClass="NaviViewController" customModule="SimpleVideoTransition" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="uf9-x0-sSc">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="jjf-eS-Sa2"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6D3-vd-i7x" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-959" y="137"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -0,0 +1,38 @@
//
// ResourceItem.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/7.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import Foundation
import MetalVideoProcess
class ResourceItem: MetalVideoEditorItem {
public var rotate = 0.0
public var scale = Position(1.0, 1.0)
//translation
public var translation = Position(0.0, 0.0)
var orientation : UIInterfaceOrientation = .portrait
weak var transformFilter: MetalVideoProcessTransformFilter?
weak var currentLayer: MetalVideoProcessBlendFilter?
var roi: CGRect = CGRect.zero
var fillType: MetalVideoProcessTransformFilter.StretchType = .aspectToFill
var isSelected: Bool = false
var startTimeText: NSString {
get {
return NSString(format: "%.2f", self.startTime.seconds)
}
}
var durationText: NSString {
get {
return NSString(format: "%.2f", self.duration.seconds)
}
}
}
@@ -0,0 +1,92 @@
//
// NaviViewController.swift
// SimpleVideoTransition
//
// Created by RenZhu Macro on 2020/7/2.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import UIKit
import MetalVideoProcess
import AVFoundation
import MetalVideoProcess
class NaviViewController: UIViewController {
@IBOutlet weak var naviStackView: UIStackView!
var transitionTimeRange = CMTimeRange.init()
let N = 14
private func getTransition(index: Int) -> (titleName: String, transObject: MetalVideoProcessTransition) {
switch index {
case 0:
return (titleName: "倒影", transObject: MetalVideoProcessReflectTransition())
case 1:
return (titleName: "闪白", transObject: MetalVideoProcessShanBaiTransition())
case 2:
return (titleName: "向下擦除", transObject: MetalVideoProcessEraseDownTransition())
case 3:
return (titleName: "叠化", transObject: MetalVideoProcessFadeTransition())
case 4:
return (titleName: "镜像翻转", transObject: MetalVideoProcessMirrorRotateTransition())
case 5:
return (titleName: "燃烧", transObject: MetalVideoProcessBurnTransition())
case 6:
return (titleName: "立方体", transObject: MetalVideoProcessCubeTransition())
case 7:
return (titleName: "向上擦除", transObject: MetalVideoProcessEraseUpTransition())
case 8:
return (titleName: "向左擦除", transObject: MetalVideoProcessEraseLeftTransition())
case 9:
return (titleName: "向右擦除", transObject: MetalVideoProcessEraseRightTransition())
case 10:
return (titleName: "变形", transObject: MetalVideoProcessMorphTransition())
case 11:
return (titleName: "圆形蒙版", transObject: MetalVideoProcessCircleEraseTransition())
case 12:
return (titleName: "上移", transObject: MetalVideoProcessVerticalUpGlitchTransition())
default:
return (titleName: "倒影", transObject: MetalVideoProcessReflectTransition())
}
}
private func getButton(index: Int) -> UIButton {
let splitNum = 5.0
let pieceWidth = Double(self.view.frame.width) / splitNum
let buttonWidth = pieceWidth * 0.8
let dIndex = Double(index)
let indexX = fmod(dIndex, splitNum) * pieceWidth
let indexY = (floor(dIndex / splitNum)) * pieceWidth + 100.0
let buttonFrame = CGRect(x: indexX, y: indexY, width: buttonWidth, height: buttonWidth)
let but = UIButton(frame: buttonFrame)
but.backgroundColor = UIColor(red: 0.3, green: 0.4, blue: 0.3, alpha: 0.7)
but.setTitle(getTransition(index: index).titleName, for: .normal)
but.titleLabel?.adjustsFontSizeToFitWidth = true
but.addTarget(self, action: #selector(self.swithController(_:)), for: .touchUpInside)
but.tag = index
return but
}
override func viewDidLoad() {
super.viewDidLoad()
for index in 0...N - 1 {
self.view.addSubview(getButton(index: index))
}
}
@IBAction func swithController(_ sender: UIButton) {
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
let trans = getTransition(index: sender.tag)
viewController.transition = trans.transObject
self.present(viewController, animated: true, completion: nil)
}
}
@@ -0,0 +1,194 @@
//
// ResourceEditorViewController.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/8.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import UIKit
import MetalVideoProcess
import AVFoundation
class ResourceItemEditView: UIViewController {
deinit {
}
public weak var resourceItem: ResourceItem?
@IBOutlet weak var timeRangeText: UILabel!
@IBOutlet weak var renderView: MetalVideoProcessRenderView!
@IBOutlet weak var startTimeSlider: UISlider!
@IBOutlet weak var durationSlider: UISlider!
@IBOutlet weak var positionSlider: UISlider!
var player: MetalVideoProcessPlayer?
var editor: MetalVideoEditor?
var assetDuration: CMTime = .zero
@IBOutlet weak var isMuteSwitch: UISwitch!
var isPipItem: Bool = false
var isImageResource: Bool = false
public func loadResourceItem(_ item: ResourceItem, isPipItem: Bool = false) {
self.isPipItem = isPipItem
self.resourceItem = item
self.isMuteSwitch.isOn = item.isMute
if let imageResource = item.resource as? ImageResource {
self.isImageResource = true
//picture
let mtlTexture = imageResource.sourceTexture(at: .zero)!
let texture = Texture(orientation: .landscapeLeft, texture: mtlTexture, timingStyle: .stillImage)
self.renderView.newTextureAvailable(texture, fromSourceIndex: 0, trackID: 0)
} else if let asset = (item.resource as? AVAssetTrackResource)?.asset {
assetDuration = asset.duration
self.editor = try? MetalVideoEditor(videoItems: [item])
guard let playerItem = self.editor?.buildPlayerItem() else {
return
}
self.player = try? MetalVideoProcessPlayer(playerItem: playerItem)
let orientation = orientationForTrack(asset: asset)
let transform = MetalVideoProcessTransformFilter()
switch orientation {
case .portrait:
break
case .landscapeLeft:
transform.rotate = 90.0
break
case .landscapeRight:
transform.rotate = -90.0
break
case .portraitUpsideDown:
transform.rotate = 180.0
break
default:
break
}
self.player?.addTarget(transform, atTargetIndex: nil, trackID: item.trackID, targetTrackId: 0)
transform --> self.renderView
} else {
return //unsupported
}
self.startTimeSlider.minimumValue = 0.0
//min value of a item is 0.1 seconds
self.durationSlider.minimumValue = 0.1
if self.isImageResource {
self.durationSlider.maximumValue = 100.0
assetDuration = CMTime(seconds: 100.0)
} else {
self.durationSlider.maximumValue = Float(assetDuration.seconds)
}
self.startTimeSlider.maximumValue = self.durationSlider.maximumValue - 0.1
self.startTimeSlider.value = Float(item.resource.selectedTimeRange.start.seconds)
self.durationSlider.value = Float(item.resource.selectedTimeRange.duration.seconds)
self.positionSlider.value = Float(item.startTime.seconds)
if self.isPipItem {
self.positionSlider.isEnabled = true
self.positionSlider.maximumValue = 100.0
self.positionSlider.value = Float(item.startTime.seconds)
}
timeRangeText.text = NSString(format: "s:%.2f d:%.2f p:%.2f",
self.startTimeSlider.value,
self.durationSlider.value,
self.positionSlider.value) as String
self.player?.seekTo(time: 0.0)
}
override func viewDidDisappear(_ animated: Bool) {
self.player?.suspend()
self.player?.dispose()
self.player = nil
self.editor = nil
}
@IBAction func positionValueChanged(_ sender: Any) {
self.resourceItem?.startTime = CMTime(seconds: self.positionSlider.value)
timeRangeText.text = NSString(format: "s:%.2f d:%.2f p:%.2f",
self.startTimeSlider.value,
self.durationSlider.value,
self.positionSlider.value) as String
}
@IBAction func startTimeValueChanged(_ sender: Any) {
let totalValue = self.startTimeSlider.value + self.durationSlider.value
if totalValue > Float(self.assetDuration.seconds) {
self.durationSlider.value = Float(self.assetDuration.seconds) - self.startTimeSlider.value
}
resourceItem?.resource.selectedTimeRange = CMTimeRange(start: CMTime(seconds: self.startTimeSlider.value),
duration: CMTime(seconds: self.durationSlider.value))
if self.isPipItem {
self.player?.seekTo(time: Float64(resourceItem?.resource.selectedTimeRange.start.seconds ?? 0.0))
} else {
self.player?.seekTo(time: Float64(self.startTimeSlider.value))
}
timeRangeText.text = NSString(format: "s:%.2f d:%.2f p:%.2f",
self.startTimeSlider.value,
self.durationSlider.value,
self.positionSlider.value) as String
}
@IBAction func DurationValueChanged(_ sender: Any) {
let totalValue = self.startTimeSlider.value + self.durationSlider.value
if totalValue > Float(self.assetDuration.seconds) {
self.startTimeSlider.value = Float(self.assetDuration.seconds) - self.durationSlider.value
}
resourceItem?.resource.selectedTimeRange = CMTimeRange(start: CMTime(seconds: self.startTimeSlider.value),
duration: CMTime(seconds: self.durationSlider.value))
timeRangeText.text = NSString(format: "s:%.2f d:%.2f p:%.2f",
self.startTimeSlider.value,
self.durationSlider.value,
self.positionSlider.value) as String
if self.isImageResource {
//No need to render the image with frameTime
return
}
self.player?.seekTo(time: Float64(self.startTimeSlider.value + self.durationSlider.value))
}
@IBAction func isMuteOn(_ sender: UISwitch) {
self.resourceItem?.isMute = sender.isOn
}
@IBAction func play(_ sender: Any) {
guard let playerItem = self.editor?.buildPlayerItem() else {
return
}
self.player?.updatePlayerItem(playerItem: playerItem)
self.player?.play()
}
@IBAction func pause(_ sender: Any) {
self.player?.pause()
}
}
@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ResourceItemEditView" customModule="SimpleVideoEditor" customModuleProvider="target">
<connections>
<outlet property="durationSlider" destination="20Y-8z-Bjc" id="TLj-DB-WrL"/>
<outlet property="isMuteSwitch" destination="Tbd-ZT-wkM" id="QeO-8E-9m5"/>
<outlet property="positionSlider" destination="d5k-3R-pnJ" id="DvC-D3-lX9"/>
<outlet property="renderView" destination="33l-v0-rcB" id="qUN-dQ-weP"/>
<outlet property="startTimeSlider" destination="BaE-Cm-F7T" id="oAw-GL-BIQ"/>
<outlet property="timeRangeText" destination="bli-Ir-iaf" id="OhB-Zg-Ggc"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="455" height="540"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IS1-mO-qkH">
<rect key="frame" x="0.0" y="0.0" width="455" height="540"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="s:0.0 d:0.0 p:0.0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bli-Ir-iaf">
<rect key="frame" x="178.5" y="10" width="98" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="33l-v0-rcB" customClass="MetalVideoProcessRenderView" customModule="MetalVideoProcess">
<rect key="frame" x="20" y="54.5" width="415" height="207.5"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="width" secondItem="33l-v0-rcB" secondAttribute="height" multiplier="2" id="OOq-Fz-7JE"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Edit TimeRange" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XLW-Lu-VBj">
<rect key="frame" x="152.5" y="25" width="138" height="19.5"/>
<constraints>
<constraint firstAttribute="width" constant="138" id="AeL-Uh-bdN"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="BaE-Cm-F7T">
<rect key="frame" x="103" y="277.5" width="344" height="31"/>
<connections>
<action selector="startTimeValueChanged:" destination="-1" eventType="valueChanged" id="0Iy-N5-Ge1"/>
</connections>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="StartTime" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nTw-8t-hm2">
<rect key="frame" x="20" y="282" width="75" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Duration" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sor-n1-AOA">
<rect key="frame" x="20" y="322.5" width="65.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Is Mute" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MW9-K9-XrY">
<rect key="frame" x="20" y="363" width="56.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="20Y-8z-Bjc">
<rect key="frame" x="103" y="318" width="344" height="31"/>
<connections>
<action selector="DurationValueChanged:" destination="-1" eventType="valueChanged" id="6Au-ng-P7h"/>
</connections>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PipPos" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gt2-Up-oP5">
<rect key="frame" x="20" y="393.5" width="52.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="d5k-3R-pnJ">
<rect key="frame" x="103" y="337.5" width="344" height="133.5"/>
<connections>
<action selector="positionValueChanged:" destination="-1" eventType="valueChanged" id="7yz-Bl-8rV"/>
</connections>
</slider>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Tbd-ZT-wkM">
<rect key="frame" x="86.5" y="358" width="51" height="31"/>
<connections>
<action selector="isMuteOn:" destination="-1" eventType="valueChanged" id="4qd-1l-V2g"/>
</connections>
</switch>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="9Lx-c3-ISl">
<rect key="frame" x="127.5" y="480" width="200" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oaz-x9-lZJ">
<rect key="frame" x="0.0" y="0.0" width="100" height="50"/>
<state key="normal" title="Play"/>
<connections>
<action selector="play:" destination="-1" eventType="touchUpInside" id="ZH2-JZ-Oe0"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bcQ-85-RzQ">
<rect key="frame" x="100" y="0.0" width="100" height="50"/>
<state key="normal" title="Pause"/>
<connections>
<action selector="pause:" destination="-1" eventType="touchUpInside" id="LaU-La-JcS"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="200" id="8AT-OT-jXM"/>
<constraint firstAttribute="height" constant="50" id="9ct-2A-OAg"/>
</constraints>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="20Y-8z-Bjc" secondAttribute="trailing" constant="10" id="1tK-Ho-DAZ"/>
<constraint firstItem="MW9-K9-XrY" firstAttribute="leading" secondItem="IS1-mO-qkH" secondAttribute="leading" constant="20" id="8Bl-kg-UIr"/>
<constraint firstItem="sor-n1-AOA" firstAttribute="top" secondItem="nTw-8t-hm2" secondAttribute="bottom" constant="20" id="8G0-jo-tS1"/>
<constraint firstItem="bli-Ir-iaf" firstAttribute="top" secondItem="IS1-mO-qkH" secondAttribute="top" constant="10" id="95P-fp-0Gf"/>
<constraint firstItem="MW9-K9-XrY" firstAttribute="top" secondItem="sor-n1-AOA" secondAttribute="bottom" constant="20" id="CcT-dv-0zy"/>
<constraint firstItem="9Lx-c3-ISl" firstAttribute="centerX" secondItem="IS1-mO-qkH" secondAttribute="centerX" id="Dxy-lQ-Lm4"/>
<constraint firstItem="33l-v0-rcB" firstAttribute="leading" secondItem="IS1-mO-qkH" secondAttribute="leading" constant="20" id="FCu-9q-iOm"/>
<constraint firstItem="sor-n1-AOA" firstAttribute="leading" secondItem="nTw-8t-hm2" secondAttribute="leading" id="Fi7-GU-iU1"/>
<constraint firstItem="nTw-8t-hm2" firstAttribute="leading" secondItem="IS1-mO-qkH" secondAttribute="leading" constant="20" id="I16-bg-OVX"/>
<constraint firstAttribute="trailing" secondItem="33l-v0-rcB" secondAttribute="trailing" constant="20" id="Ige-mZ-iAm"/>
<constraint firstItem="bli-Ir-iaf" firstAttribute="centerX" secondItem="IS1-mO-qkH" secondAttribute="centerX" id="Iv0-mA-qmg"/>
<constraint firstItem="d5k-3R-pnJ" firstAttribute="centerY" secondItem="gt2-Up-oP5" secondAttribute="centerY" id="LM2-Im-44w"/>
<constraint firstItem="d5k-3R-pnJ" firstAttribute="width" secondItem="20Y-8z-Bjc" secondAttribute="width" id="LVm-d0-8dR"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="gt2-Up-oP5" secondAttribute="bottom" constant="10" id="QUg-zR-ipe"/>
<constraint firstAttribute="trailing" secondItem="BaE-Cm-F7T" secondAttribute="trailing" constant="10" id="RBN-jo-twB"/>
<constraint firstAttribute="trailing" secondItem="d5k-3R-pnJ" secondAttribute="trailing" constant="10" id="RBN-l1-Kbw"/>
<constraint firstItem="BaE-Cm-F7T" firstAttribute="centerY" secondItem="nTw-8t-hm2" secondAttribute="centerY" id="Vty-Xf-bzY"/>
<constraint firstItem="BaE-Cm-F7T" firstAttribute="leading" secondItem="nTw-8t-hm2" secondAttribute="trailing" constant="10" id="Vz0-dI-O0O"/>
<constraint firstItem="9Lx-c3-ISl" firstAttribute="top" secondItem="d5k-3R-pnJ" secondAttribute="bottom" constant="10" id="WSu-0F-xF0"/>
<constraint firstItem="20Y-8z-Bjc" firstAttribute="width" secondItem="BaE-Cm-F7T" secondAttribute="width" id="XH2-i9-bU4"/>
<constraint firstItem="Tbd-ZT-wkM" firstAttribute="leading" secondItem="MW9-K9-XrY" secondAttribute="trailing" constant="10" id="azZ-Ho-4Qs"/>
<constraint firstItem="Tbd-ZT-wkM" firstAttribute="centerY" secondItem="MW9-K9-XrY" secondAttribute="centerY" id="cAG-JT-Czu"/>
<constraint firstItem="gt2-Up-oP5" firstAttribute="leading" secondItem="IS1-mO-qkH" secondAttribute="leading" constant="20" id="cAr-6U-TbS"/>
<constraint firstItem="20Y-8z-Bjc" firstAttribute="centerY" secondItem="sor-n1-AOA" secondAttribute="centerY" id="e8d-yu-mVB"/>
<constraint firstItem="gt2-Up-oP5" firstAttribute="top" secondItem="MW9-K9-XrY" secondAttribute="bottom" constant="10" id="edR-PI-BxV"/>
<constraint firstItem="nTw-8t-hm2" firstAttribute="top" secondItem="33l-v0-rcB" secondAttribute="bottom" constant="20" id="gdq-Qh-R11"/>
<constraint firstItem="33l-v0-rcB" firstAttribute="top" secondItem="XLW-Lu-VBj" secondAttribute="bottom" constant="10" id="kzg-iM-HlJ"/>
<constraint firstAttribute="bottom" secondItem="9Lx-c3-ISl" secondAttribute="bottom" constant="10" id="lqB-g6-vhJ"/>
<constraint firstItem="XLW-Lu-VBj" firstAttribute="top" secondItem="IS1-mO-qkH" secondAttribute="top" constant="25" id="mf9-b7-p4a"/>
<constraint firstItem="XLW-Lu-VBj" firstAttribute="centerX" secondItem="IS1-mO-qkH" secondAttribute="centerX" constant="-6" id="pR8-bb-Q05"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="450" id="xGd-EA-30x"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="IS1-mO-qkH" secondAttribute="bottom" id="d8w-V8-fpT"/>
<constraint firstItem="IS1-mO-qkH" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="nIN-WD-C0j"/>
<constraint firstItem="IS1-mO-qkH" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="ncB-9S-Iub"/>
<constraint firstAttribute="trailing" secondItem="IS1-mO-qkH" secondAttribute="trailing" id="rlD-9w-zuN"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="380.43478260869568" y="387.05357142857139"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VAH-Ff-ctu">
<rect key="frame" x="0.0" y="0.0" width="42" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<point key="canvasLocation" x="197" y="623"/>
</label>
</objects>
</document>
@@ -0,0 +1,25 @@
//
// ResourceItemTableViewCell.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/8.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import UIKit
class ResourceItemTableViewCell: UITableViewCell {
@IBOutlet weak var trackIdLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
@@ -0,0 +1,10 @@
//
// Header.h
// SimpleVideoExport
//
// Created by RenZhu Macro on 2020/7/7.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
#import "ProgressHUD.h"
#import "MBProgressHUD.h"
@@ -0,0 +1,131 @@
//
// BlurLayer.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/14.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
private extension CGRect {
func rectangle(_ s: CGSize) -> CGRect {
let x = origin.x / s.width
let y = origin.y / s.height
let width = size.width / s.width
let height = size.height / s.height
return CGRect(x: x, y: y, width: width, height: height)
}
}
class BlurLayer: CALayer {
private static let blurRadiusKey = "blurRadius"
private static let blurLayoutKey = "blurLayout"
@NSManaged var blurRadius: CGFloat
@NSManaged private var blurLayout: CGFloat
private var fromBlurRadius: CGFloat?
var presentationRadius: CGFloat {
if let radius = fromBlurRadius {
if let layer = presentation() {
return layer.blurRadius
} else {
return radius
}
} else {
return blurRadius
}
}
override class func needsDisplay(forKey key: String) -> Bool {
if key == blurRadiusKey || key == blurLayoutKey {
return true
}
return super.needsDisplay(forKey: key)
}
open override func action(forKey event: String) -> CAAction? {
if event == BlurLayer.blurRadiusKey {
fromBlurRadius = nil
if let action = super.action(forKey: "opacity") as? CABasicAnimation {
fromBlurRadius = (presentation() ?? self).blurRadius
action.keyPath = event
action.fromValue = fromBlurRadius
return action
}
}
if event == BlurLayer.blurLayoutKey, let action = super.action(forKey: "opacity") as? CABasicAnimation {
action.keyPath = event
action.fromValue = 0
action.toValue = 1
return action
}
return super.action(forKey: event)
}
}
extension BlurLayer {
func draw(_ image: UIImage, fixes isFixes: Bool, baseLayer: CALayer?) {
contents = image.cgImage
contentsScale = image.scale
if isFixes, let blurLayer = presentation() {
contentsRect = blurLayer.convert(blurLayer.bounds, to: baseLayer).rectangle(image.size)
}
}
func refresh() {
fromBlurRadius = nil
}
func animate() {
UIView.performWithoutAnimation {
blurLayout = 0
}
blurLayout = 1
}
func render(in context: CGContext, for layer: CALayer) {
let layers = hideOverlappingLayers(layer.sublayers)
layer.render(in: context)
layers.forEach {
$0.isHidden = false
}
}
private func hideOverlappingLayers(_ layers: [CALayer]?) -> [CALayer] {
var hiddenLayers: [CALayer] = []
guard let layers = layers else {
return hiddenLayers
}
for layer in layers.reversed() {
if isHang(to: layer) {
return hiddenLayers + hideOverlappingLayers(layer.sublayers)
}
if layer.isHidden == false {
layer.isHidden = true
hiddenLayers.append(layer)
}
if layer == self {
return hiddenLayers
}
}
return hiddenLayers
}
private func isHang(to target: CALayer) -> Bool {
var layer = superlayer
while layer != nil {
if layer == target {
return true
}
layer = layer?.superlayer
}
return false
}
}
@@ -0,0 +1,31 @@
//
// CGContext+CGImage.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
extension CGContext {
static func imageContext(with quality: CaptureQuality, rect: CGRect, opaque: Bool) -> CGContext? {
UIGraphicsBeginImageContextWithOptions(rect.size, opaque, quality.imageScale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
context.interpolationQuality = quality.interpolationQuality
return context
}
func makeImage(with blendColor: UIColor?, blendMode: CGBlendMode, size: CGSize) -> CGImage? {
if let color = blendColor {
setFillColor(color.cgColor)
setBlendMode(blendMode)
fill(CGRect(origin: .zero, size: size))
}
return makeImage()
}
}
@@ -0,0 +1,65 @@
//
// CGImage+Accelerate.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import Accelerate
extension CGImage {
var area: Int {
return width * height
}
private var size: CGSize {
return CGSize(width: width, height: height)
}
private var bytes: Int {
return bytesPerRow * height
}
private func imageBuffer(from data: UnsafeMutableRawPointer!) -> vImage_Buffer {
return vImage_Buffer(data: data, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
}
func blurred(with boxSize: UInt32, iterations: Int, blendColor: UIColor?, blendMode: CGBlendMode) -> CGImage? {
guard let providerData = dataProvider?.data else {
return nil
}
let inData = malloc(bytes)
var inBuffer = imageBuffer(from: inData)
let outData = malloc(bytes)
var outBuffer = imageBuffer(from: outData)
let tempSize = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, boxSize, boxSize, nil, vImage_Flags(kvImageEdgeExtend + kvImageGetTempBufferSize))
let tempData = malloc(tempSize)
defer {
free(inData)
free(outData)
free(tempData)
}
let source = CFDataGetBytePtr(providerData)
memcpy(inBuffer.data, source, bytes)
for _ in 0..<iterations {
vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, tempData, 0, 0, boxSize, boxSize, nil, vImage_Flags(kvImageEdgeExtend))
let temp = inBuffer.data
inBuffer.data = outBuffer.data
outBuffer.data = temp
}
let context = colorSpace.flatMap {
CGContext(data: inBuffer.data, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: $0, bitmapInfo: bitmapInfo.rawValue)
}
return context?.makeImage(with: blendColor, blendMode: blendMode, size: size)
}
}
@@ -0,0 +1,32 @@
//
// CaptureQuality.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
public enum CaptureQuality {
case `default`
case low
case medium
case high
var imageScale: CGFloat {
switch self {
case .default, .high:
return 0
case .low, .medium:
return 1
}
}
var interpolationQuality: CGInterpolationQuality {
switch self {
case .default, .low:
return .none
case .medium, .high:
return .default
}
}
}
@@ -0,0 +1,19 @@
//
// DynamicBlurView.h
// DynamicBlurView
//
// Created by Kyohei Ito on 2015/04/08.
// Copyright (c) 2015年 kyohei_ito. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for DynamicBlurView.
FOUNDATION_EXPORT double DynamicBlurViewVersionNumber;
//! Project version string for DynamicBlurView.
FOUNDATION_EXPORT const unsigned char DynamicBlurViewVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DynamicBlurView/PublicHeader.h>
@@ -0,0 +1,195 @@
//
// DynamicBlurView.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2015/04/08.
// Copyright (c) 2015 kyohei_ito. All rights reserved.
//
import UIKit
open class DynamicBlurView: UIView {
open override class var layerClass : AnyClass {
return BlurLayer.self
}
private var staticImage: UIImage?
private var displayLink: CADisplayLink?
private var blurLayer: BlurLayer {
return layer as! BlurLayer
}
private let mainQueue = DispatchQueue.main
private let globalQueue: DispatchQueue = {
if #available (iOS 8.0, *) {
return .global(qos: .userInteractive)
} else {
return .global(priority: .high)
}
}()
private var renderingTarget: UIView? {
if isDeepRendering {
return window
} else {
return superview
}
}
/// When true, it captures displays image and blur it asynchronously. Try to set true if needs more performance.
/// Asynchronous drawing is possibly crash when needs to process on main thread that drawing with animation for example.
open var drawsAsynchronously: Bool = false
/// Radius of blur.
open var blurRadius: CGFloat {
set { blurLayer.blurRadius = newValue }
get { return blurLayer.blurRadius }
}
/// Default is none.
open var trackingMode: TrackingMode = .none {
didSet {
if trackingMode != oldValue {
linkForDisplay()
}
}
}
/// Blend color.
open var blendColor: UIColor?
/// Blend mode.
open var blendMode: CGBlendMode = .plusLighter
/// Default is 3.
open var iterations: Int = 3
/// If the view want to render beyond the layer, should be true.
open var isDeepRendering: Bool = false
/// When none of tracking mode, it can change the radius of blur with the ratio. Should set from 0 to 1.
open var blurRatio: CGFloat = 1 {
didSet {
if let image = staticImage, oldValue != blurRatio {
draw(image, blurRadius: blurRadius, fixes: false, baseLayer: renderingTarget?.layer)
}
}
}
/// Quality of captured image.
open var quality: CaptureQuality = .medium
public override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = false
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
isUserInteractionEnabled = false
}
open override func didMoveToWindow() {
super.didMoveToWindow()
if let view = renderingTarget, window != nil && trackingMode == .none {
staticImage = snapshotImage(for: view.layer, conversion: !isDeepRendering)
}
}
open override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
displayLink?.invalidate()
displayLink = nil
} else {
linkForDisplay()
}
}
private func async(on queue: DispatchQueue, actions: @escaping () -> Void) {
if drawsAsynchronously {
queue.async(execute: actions)
} else {
actions()
}
}
private func sync(on queue: DispatchQueue, actions: () -> Void) {
if drawsAsynchronously {
queue.sync(execute: actions)
} else {
actions()
}
}
private func draw(_ image: UIImage, blurRadius radius: CGFloat, fixes isFixes: Bool, baseLayer: CALayer?) {
async(on: globalQueue) { [weak self] in
if let me = self, let blurredImage = image.blurred(radius: radius, iterations: me.iterations, ratio: me.blurRatio, blendColor: me.blendColor, blendMode: me.blendMode) {
me.sync(on: me.mainQueue) {
me.blurLayer.draw(blurredImage, fixes: isFixes, baseLayer: baseLayer)
}
}
}
}
private func blurLayerRect(to layer: CALayer, conversion: Bool) -> CGRect {
if conversion {
let presentationLayer = blurLayer.presentation() ?? blurLayer
return presentationLayer.convert(presentationLayer.bounds, to: layer)
} else {
return layer.bounds
}
}
private func snapshotImage(for layer: CALayer, conversion: Bool) -> UIImage? {
let rect = blurLayerRect(to: layer, conversion: conversion)
guard let context = CGContext.imageContext(with: quality, rect: rect, opaque: isOpaque) else {
return nil
}
blurLayer.render(in: context, for: layer)
defer {
UIGraphicsEndImageContext()
}
return UIGraphicsGetImageFromCurrentImageContext()
}
}
extension DynamicBlurView {
open override func display(_ layer: CALayer) {
let blurRadius = blurLayer.presentationRadius
let isFixes = isDeepRendering && staticImage != nil
if let view = renderingTarget, let image = staticImage ?? snapshotImage(for: view.layer, conversion: !isFixes) {
draw(image, blurRadius: blurRadius, fixes: isFixes, baseLayer: view.layer)
}
}
}
extension DynamicBlurView {
private func linkForDisplay() {
displayLink?.invalidate()
displayLink = UIScreen.main.displayLink(withTarget: self, selector: #selector(DynamicBlurView.displayDidRefresh(_:)))
displayLink?.add(to: .main, forMode: RunLoop.Mode(rawValue: trackingMode.description))
}
@objc private func displayDidRefresh(_ displayLink: CADisplayLink) {
display(layer)
}
}
extension DynamicBlurView {
/// Remove cache of blur image then get it again.
open func refresh() {
blurLayer.refresh()
staticImage = nil
blurRatio = 1
display(layer)
}
/// Remove cache of blur image.
open func remove() {
blurLayer.refresh()
staticImage = nil
blurRatio = 1
layer.contents = nil
}
/// Should use when needs to change layout with animation when is set none of tracking mode.
public func animate() {
blurLayer.animate()
}
}
@@ -0,0 +1,25 @@
//
// TrackingMode.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
public enum TrackingMode: CustomStringConvertible {
case tracking
case common
case none
public var description: String {
switch self {
case .tracking:
return RunLoop.Mode.tracking.rawValue
case .common:
return RunLoop.Mode.common.rawValue
case .none:
return ""
}
}
}
@@ -0,0 +1,28 @@
//
// UIImage+Blur.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/11.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
public extension UIImage {
func blurred(radius: CGFloat, iterations: Int, ratio: CGFloat, blendColor color: UIColor?, blendMode mode: CGBlendMode) -> UIImage? {
guard let cgImage = cgImage else {
return nil
}
if cgImage.area <= 0 || radius <= 0 {
return self
}
var boxSize = UInt32(radius * scale * ratio)
if boxSize % 2 == 0 {
boxSize += 1
}
return cgImage.blurred(with: boxSize, iterations: iterations, blendColor: color, blendMode: mode).map {
UIImage(cgImage: $0, scale: scale, orientation: imageOrientation)
}
}
}
@@ -0,0 +1,86 @@
//
// PopupDialogInteractiveTransition.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
// Handles interactive transition triggered via pan gesture recognizer on dialog
final internal class InteractiveTransition: UIPercentDrivenInteractiveTransition {
// If the interactive transition was started
var hasStarted = false
// If the interactive transition
var shouldFinish = false
// The view controller containing the views
// with attached gesture recognizers
weak var viewController: UIViewController?
@objc func handlePan(_ sender: UIPanGestureRecognizer) {
guard let vc = viewController else { return }
guard let progress = calculateProgress(sender: sender) else { return }
switch sender.state {
case .began:
hasStarted = true
vc.dismiss(animated: true, completion: nil)
case .changed:
shouldFinish = progress > 0.3
update(progress)
case .cancelled:
hasStarted = false
cancel()
case .ended:
hasStarted = false
completionSpeed = 0.55
shouldFinish ? finish() : cancel()
default:
break
}
}
}
internal extension InteractiveTransition {
/*!
Translates the pan gesture recognizer position to the progress percentage
- parameter sender: A UIPanGestureRecognizer
- returns: Progress
*/
func calculateProgress(sender: UIPanGestureRecognizer) -> CGFloat? {
guard let vc = viewController else { return nil }
// http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/
let translation = sender.translation(in: vc.view)
let verticalMovement = translation.y / vc.view.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
return progress
}
}
@@ -0,0 +1,132 @@
//
// PopupDialog+Keyboard.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// This extension is designed to handle dialog positioning
/// if a keyboard is displayed while the popup is on top
internal extension PopupDialog {
// MARK: - Keyboard & orientation observers
/*! Add obserservers for UIKeyboard notifications */
func addObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged),
name: UIDevice.orientationDidChangeNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillChangeFrame),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
}
/*! Remove observers */
func removeObservers() {
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
NotificationCenter.default.removeObserver(self,
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.removeObserver(self,
name: UIResponder.keyboardWillHideNotification,
object: nil)
NotificationCenter.default.removeObserver(self,
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
}
// MARK: - Actions
/*!
Keyboard will show notification listener
- parameter notification: NSNotification
*/
@objc fileprivate func keyboardWillShow(_ notification: Notification) {
guard isTopAndVisible else { return }
keyboardShown = true
centerPopup()
}
/*!
Keyboard will hide notification listener
- parameter notification: NSNotification
*/
@objc fileprivate func keyboardWillHide(_ notification: Notification) {
guard isTopAndVisible else { return }
keyboardShown = false
centerPopup()
}
/*!
Keyboard will change frame notification listener
- parameter notification: NSNotification
*/
@objc fileprivate func keyboardWillChangeFrame(_ notification: Notification) {
guard let keyboardRect = (notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
keyboardHeight = keyboardRect.cgRectValue.height
}
/*!
Listen to orientation changes
- parameter notification: NSNotification
*/
@objc fileprivate func orientationChanged(_ notification: Notification) {
if keyboardShown { centerPopup() }
}
fileprivate func centerPopup() {
// Make sure keyboard should reposition on keayboard notifications
guard keyboardShiftsView else { return }
// Make sure a valid keyboard height is available
guard let keyboardHeight = keyboardHeight else { return }
// Calculate new center of shadow background
let popupCenter = keyboardShown ? keyboardHeight / -2 : 0
// Reposition and animate
popupContainerView.centerYConstraint?.constant = popupCenter
popupContainerView.pv_layoutIfNeededAnimated()
}
}
@@ -0,0 +1,342 @@
//
// PopupDialog.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// Creates a Popup dialog similar to UIAlertController
final public class PopupDialog: UIViewController {
// MARK: Private / Internal
/// First init flag
fileprivate var initialized = false
/// StatusBar display related
fileprivate let hideStatusBar: Bool
fileprivate var statusBarShouldBeHidden: Bool = false
/// Width for iPad displays
fileprivate let preferredWidth: CGFloat
/// The completion handler
fileprivate var completion: (() -> Void)?
/// The custom transition presentation manager
fileprivate var presentationManager: PresentationManager!
/// Interactor class for pan gesture dismissal
fileprivate lazy var interactor = InteractiveTransition()
/// Returns the controllers view
internal var popupContainerView: PopupDialogContainerView {
return view as! PopupDialogContainerView // swiftlint:disable:this force_cast
}
/// The set of buttons
fileprivate var buttons = [PopupDialogButton]()
/// Whether keyboard has shifted view
internal var keyboardShown = false
/// Keyboard height
internal var keyboardHeight: CGFloat?
// MARK: Public
/// The content view of the popup dialog
public var viewController: UIViewController
/// Whether or not to shift view for keyboard display
public var keyboardShiftsView = true
// MARK: - Initializers
/*!
Creates a standard popup dialog with title, message and image field
- parameter title: The dialog title
- parameter message: The dialog message
- parameter image: The dialog image
- parameter buttonAlignment: The dialog button alignment
- parameter transitionStyle: The dialog transition style
- parameter preferredWidth: The preferred width for iPad screens
- parameter tapGestureDismissal: Indicates if dialog can be dismissed via tap gesture
- parameter panGestureDismissal: Indicates if dialog can be dismissed via pan gesture
- parameter hideStatusBar: Whether to hide the status bar on PopupDialog presentation
- parameter completion: Completion block invoked when dialog was dismissed
- returns: Popup dialog default style
*/
@objc public convenience init(
title: String?,
message: String?,
image: UIImage? = nil,
buttonAlignment: NSLayoutConstraint.Axis = .vertical,
transitionStyle: PopupDialogTransitionStyle = .bounceUp,
preferredWidth: CGFloat = 340,
tapGestureDismissal: Bool = true,
panGestureDismissal: Bool = true,
hideStatusBar: Bool = false,
completion: (() -> Void)? = nil) {
// Create and configure the standard popup dialog view
let viewController = PopupDialogDefaultViewController()
viewController.titleText = title
viewController.messageText = message
viewController.image = image
// Call designated initializer
self.init(viewController: viewController,
buttonAlignment: buttonAlignment,
transitionStyle: transitionStyle,
preferredWidth: preferredWidth,
tapGestureDismissal: tapGestureDismissal,
panGestureDismissal: panGestureDismissal,
hideStatusBar: hideStatusBar,
completion: completion)
}
/*!
Creates a popup dialog containing a custom view
- parameter viewController: A custom view controller to be displayed
- parameter buttonAlignment: The dialog button alignment
- parameter transitionStyle: The dialog transition style
- parameter preferredWidth: The preferred width for iPad screens
- parameter tapGestureDismissal: Indicates if dialog can be dismissed via tap gesture
- parameter panGestureDismissal: Indicates if dialog can be dismissed via pan gesture
- parameter hideStatusBar: Whether to hide the status bar on PopupDialog presentation
- parameter completion: Completion block invoked when dialog was dismissed
- returns: Popup dialog with a custom view controller
*/
@objc public init(
viewController: UIViewController,
buttonAlignment: NSLayoutConstraint.Axis = .vertical,
transitionStyle: PopupDialogTransitionStyle = .bounceUp,
preferredWidth: CGFloat = 340,
tapGestureDismissal: Bool = true,
panGestureDismissal: Bool = true,
hideStatusBar: Bool = false,
completion: (() -> Void)? = nil) {
self.viewController = viewController
self.preferredWidth = preferredWidth
self.hideStatusBar = hideStatusBar
self.completion = completion
super.init(nibName: nil, bundle: nil)
// Init the presentation manager
presentationManager = PresentationManager(transitionStyle: transitionStyle, interactor: interactor)
// Assign the interactor view controller
interactor.viewController = self
// Define presentation styles
transitioningDelegate = presentationManager
modalPresentationStyle = .custom
// StatusBar setup
modalPresentationCapturesStatusBarAppearance = true
// Add our custom view to the container
addChild(viewController)
popupContainerView.stackView.insertArrangedSubview(viewController.view, at: 0)
popupContainerView.buttonStackView.axis = buttonAlignment
viewController.didMove(toParent: self)
// Allow for dialog dismissal on background tap
if tapGestureDismissal {
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapRecognizer.cancelsTouchesInView = false
popupContainerView.addGestureRecognizer(tapRecognizer)
}
// Allow for dialog dismissal on dialog pan gesture
if panGestureDismissal {
let panRecognizer = UIPanGestureRecognizer(target: interactor, action: #selector(InteractiveTransition.handlePan))
panRecognizer.cancelsTouchesInView = false
popupContainerView.stackView.addGestureRecognizer(panRecognizer)
}
}
// Init with coder not implemented
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View life cycle
/// Replaces controller view with popup view
public override func loadView() {
view = PopupDialogContainerView(frame: UIScreen.main.bounds, preferredWidth: preferredWidth)
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addObservers()
guard !initialized else { return }
appendButtons()
initialized = true
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
statusBarShouldBeHidden = hideStatusBar
UIView.animate(withDuration: 0.15) {
self.setNeedsStatusBarAppearanceUpdate()
}
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObservers()
}
deinit {
completion?()
completion = nil
}
// MARK: - Dismissal related
@objc fileprivate func handleTap(_ sender: UITapGestureRecognizer) {
// Make sure it's not a tap on the dialog but the background
let point = sender.location(in: popupContainerView.stackView)
guard !popupContainerView.stackView.point(inside: point, with: nil) else { return }
dismiss()
}
/*!
Dismisses the popup dialog
*/
@objc public func dismiss(_ completion: (() -> Void)? = nil) {
self.dismiss(animated: true) {
completion?()
}
}
// MARK: - Button related
/*!
Appends the buttons added to the popup dialog
to the placeholder stack view
*/
fileprivate func appendButtons() {
// Add action to buttons
let stackView = popupContainerView.stackView
let buttonStackView = popupContainerView.buttonStackView
if buttons.isEmpty {
stackView.removeArrangedSubview(popupContainerView.buttonStackView)
}
for (index, button) in buttons.enumerated() {
button.needsLeftSeparator = buttonStackView.axis == .horizontal && index > 0
buttonStackView.addArrangedSubview(button)
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
}
/*!
Adds a single PopupDialogButton to the Popup dialog
- parameter button: A PopupDialogButton instance
*/
@objc public func addButton(_ button: PopupDialogButton) {
buttons.append(button)
}
/*!
Adds an array of PopupDialogButtons to the Popup dialog
- parameter buttons: A list of PopupDialogButton instances
*/
@objc public func addButtons(_ buttons: [PopupDialogButton]) {
self.buttons += buttons
}
/// Calls the action closure of the button instance tapped
@objc fileprivate func buttonTapped(_ button: PopupDialogButton) {
if button.dismissOnTap {
dismiss({ button.buttonAction?() })
} else {
button.buttonAction?()
}
}
/*!
Simulates a button tap for the given index
Makes testing a breeze
- parameter index: The index of the button to tap
*/
public func tapButtonWithIndex(_ index: Int) {
let button = buttons[index]
button.buttonAction?()
}
// MARK: - StatusBar display related
public override var prefersStatusBarHidden: Bool {
return statusBarShouldBeHidden
}
public override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
}
// MARK: - View proxy values
extension PopupDialog {
/// The button alignment of the alert dialog
@objc public var buttonAlignment: NSLayoutConstraint.Axis {
get {
return popupContainerView.buttonStackView.axis
}
set {
popupContainerView.buttonStackView .axis = newValue
popupContainerView.pv_layoutIfNeededAnimated()
}
}
/// The transition style
@objc public var transitionStyle: PopupDialogTransitionStyle {
get { return presentationManager.transitionStyle }
set { presentationManager.transitionStyle = newValue }
}
}
// MARK: - Shake
extension PopupDialog {
/// Performs a shake animation on the dialog
@objc public func shake() {
popupContainerView.pv_shake()
}
}
@@ -0,0 +1,166 @@
//
// PopupDialogButton.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// Represents the default button for the popup dialog
open class PopupDialogButton: UIButton {
public typealias PopupDialogButtonAction = () -> Void
// MARK: Public
/// The font and size of the button title
@objc open dynamic var titleFont: UIFont? {
get { return titleLabel?.font }
set { titleLabel?.font = newValue }
}
/// The height of the button
@objc open dynamic var buttonHeight: Int
/// The title color of the button
@objc open dynamic var titleColor: UIColor? {
get { return self.titleColor(for: UIControl.State()) }
set { setTitleColor(newValue, for: UIControl.State()) }
}
/// The background color of the button
@objc open dynamic var buttonColor: UIColor? {
get { return backgroundColor }
set { backgroundColor = newValue }
}
/// The separator color of this button
@objc open dynamic var separatorColor: UIColor? {
get { return separator.backgroundColor }
set {
separator.backgroundColor = newValue
leftSeparator.backgroundColor = newValue
}
}
/// Default appearance of the button
open var defaultTitleFont = UIFont.systemFont(ofSize: 14)
open var defaultTitleColor = UIColor(red: 0.25, green: 0.53, blue: 0.91, alpha: 1)
open var defaultButtonColor = UIColor.clear
open var defaultSeparatorColor = UIColor(white: 0.9, alpha: 1)
/// Whether button should dismiss popup when tapped
@objc open var dismissOnTap = true
/// The action called when the button is tapped
open fileprivate(set) var buttonAction: PopupDialogButtonAction?
// MARK: Private
fileprivate lazy var separator: UIView = {
let line = UIView(frame: .zero)
line.translatesAutoresizingMaskIntoConstraints = false
return line
}()
fileprivate lazy var leftSeparator: UIView = {
let line = UIView(frame: .zero)
line.translatesAutoresizingMaskIntoConstraints = false
line.alpha = 0
return line
}()
// MARK: Internal
internal var needsLeftSeparator: Bool = false {
didSet {
leftSeparator.alpha = needsLeftSeparator ? 1.0 : 0.0
}
}
// MARK: Initializers
/*!
Creates a button that can be added to the popup dialog
- parameter title: The button title
- parameter dismisssOnTap: Whether a tap automatically dismisses the dialog
- parameter action: The action closure
- returns: PopupDialogButton
*/
@objc public init(title: String, height: Int = 45, dismissOnTap: Bool = true, action: PopupDialogButtonAction?) {
// Assign the button height
buttonHeight = height
// Assign the button action
buttonAction = action
super.init(frame: .zero)
// Set the button title
setTitle(title, for: UIControl.State())
self.dismissOnTap = dismissOnTap
// Setup the views
setupView()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: View setup
open func setupView() {
// Default appearance
setTitleColor(defaultTitleColor, for: UIControl.State())
titleLabel?.font = defaultTitleFont
backgroundColor = defaultButtonColor
separator.backgroundColor = defaultSeparatorColor
leftSeparator.backgroundColor = defaultSeparatorColor
// Add and layout views
addSubview(separator)
addSubview(leftSeparator)
let views = ["separator": separator, "leftSeparator": leftSeparator, "button": self]
let metrics = ["buttonHeight": buttonHeight]
var constraints = [NSLayoutConstraint]()
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:[button(buttonHeight)]", options: [], metrics: metrics, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[separator]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[separator(1)]", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[leftSeparator(1)]", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[leftSeparator]|", options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
}
open override var isHighlighted: Bool {
didSet {
isHighlighted ? pv_fade(.out, 0.5) : pv_fade(.in, 1.0)
}
}
}
@@ -0,0 +1,197 @@
//
// PopupDialogContainerView.swift
// Pods
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// The main view of the popup dialog
final public class PopupDialogContainerView: UIView {
// MARK: - Appearance
/// The background color of the popup dialog
override public dynamic var backgroundColor: UIColor? {
get { return container.backgroundColor }
set { container.backgroundColor = newValue }
}
/// The corner radius of the popup view
@objc public dynamic var cornerRadius: Float {
get { return Float(shadowContainer.layer.cornerRadius) }
set {
let radius = CGFloat(newValue)
shadowContainer.layer.cornerRadius = radius
container.layer.cornerRadius = radius
}
}
// MARK: Shadow related
/// Enable / disable shadow rendering of the container
@objc public dynamic var shadowEnabled: Bool {
get { return shadowContainer.layer.shadowRadius > 0 }
set { shadowContainer.layer.shadowRadius = newValue ? shadowRadius : 0 }
}
/// Color of the container shadow
@objc public dynamic var shadowColor: UIColor? {
get {
guard let color = shadowContainer.layer.shadowColor else {
return nil
}
return UIColor(cgColor: color)
}
set { shadowContainer.layer.shadowColor = newValue?.cgColor }
}
/// Radius of the container shadow
@objc public dynamic var shadowRadius: CGFloat {
get { return shadowContainer.layer.shadowRadius }
set { shadowContainer.layer.shadowRadius = newValue }
}
/// Opacity of the the container shadow
@objc public dynamic var shadowOpacity: Float {
get { return shadowContainer.layer.shadowOpacity }
set { shadowContainer.layer.shadowOpacity = newValue }
}
/// Offset of the the container shadow
@objc public dynamic var shadowOffset: CGSize {
get { return shadowContainer.layer.shadowOffset }
set { shadowContainer.layer.shadowOffset = newValue }
}
/// Path of the the container shadow
@objc public dynamic var shadowPath: CGPath? {
get { return shadowContainer.layer.shadowPath}
set { shadowContainer.layer.shadowPath = newValue }
}
// MARK: - Views
/// The shadow container is the basic view of the PopupDialog
/// As it does not clip subviews, a shadow can be applied to it
internal lazy var shadowContainer: UIView = {
let shadowContainer = UIView(frame: .zero)
shadowContainer.translatesAutoresizingMaskIntoConstraints = false
shadowContainer.backgroundColor = UIColor.clear
shadowContainer.layer.shadowColor = UIColor.black.cgColor
shadowContainer.layer.shadowRadius = 5
shadowContainer.layer.shadowOpacity = 0.4
shadowContainer.layer.shadowOffset = CGSize(width: 0, height: 0)
shadowContainer.layer.cornerRadius = 4
return shadowContainer
}()
/// The container view is a child of shadowContainer and contains
/// all other views. It clips to bounds so cornerRadius can be set
internal lazy var container: UIView = {
let container = UIView(frame: .zero)
container.translatesAutoresizingMaskIntoConstraints = false
container.backgroundColor = UIColor.white
container.clipsToBounds = true
container.layer.cornerRadius = 4
return container
}()
// The container stack view for buttons
internal lazy var buttonStackView: UIStackView = {
let buttonStackView = UIStackView()
buttonStackView.translatesAutoresizingMaskIntoConstraints = false
buttonStackView.distribution = .fillEqually
buttonStackView.spacing = 0
return buttonStackView
}()
// The main stack view, containing all relevant views
internal lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [self.buttonStackView])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 0
return stackView
}()
// The preferred width for iPads
fileprivate let preferredWidth: CGFloat
// MARK: - Constraints
/// The center constraint of the shadow container
internal var centerYConstraint: NSLayoutConstraint?
// MARK: - Initializers
internal init(frame: CGRect, preferredWidth: CGFloat) {
self.preferredWidth = preferredWidth
super.init(frame: frame)
setupViews()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View setup
internal func setupViews() {
// Add views
addSubview(shadowContainer)
shadowContainer.addSubview(container)
container.addSubview(stackView)
// Layout views
let views = ["shadowContainer": shadowContainer, "container": container, "stackView": stackView]
var constraints = [NSLayoutConstraint]()
// Shadow container constraints
if UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad {
let metrics = ["preferredWidth": preferredWidth]
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-(>=40)-[shadowContainer(==preferredWidth@900)]-(>=40)-|", options: [], metrics: metrics, views: views)
} else {
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-(>=10,==20@900)-[shadowContainer(<=340,>=300)]-(>=10,==20@900)-|", options: [], metrics: nil, views: views)
}
constraints += [NSLayoutConstraint(item: shadowContainer, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0)]
centerYConstraint = NSLayoutConstraint(item: shadowContainer, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
if let centerYConstraint = centerYConstraint {
constraints.append(centerYConstraint)
}
// Container constraints
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[container]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[container]|", options: [], metrics: nil, views: views)
// Main stack view constraints
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[stackView]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[stackView]|", options: [], metrics: nil, views: views)
// Activate constraints
NSLayoutConstraint.activate(constraints)
}
}
@@ -0,0 +1,54 @@
//
// PopupDialogDefaultButtons.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
// MARK: Default button
/// Represents the default button for the popup dialog
public final class DefaultButton: PopupDialogButton {}
// MARK: Cancel button
/// Represents a cancel button for the popup dialog
public final class CancelButton: PopupDialogButton {
override public func setupView() {
defaultTitleColor = UIColor.lightGray
super.setupView()
}
}
// MARK: destructive button
/// Represents a destructive button for the popup dialog
public final class DestructiveButton: PopupDialogButton {
override public func setupView() {
defaultTitleColor = UIColor.red
super.setupView()
}
}
@@ -0,0 +1,148 @@
//
// PopupDialogView.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// The main view of the popup dialog
final public class PopupDialogDefaultView: UIView {
// MARK: - Appearance
/// The font and size of the title label
@objc public dynamic var titleFont: UIFont {
get { return titleLabel.font }
set { titleLabel.font = newValue }
}
/// The color of the title label
@objc public dynamic var titleColor: UIColor? {
get { return titleLabel.textColor }
set { titleLabel.textColor = newValue }
}
/// The text alignment of the title label
@objc public dynamic var titleTextAlignment: NSTextAlignment {
get { return titleLabel.textAlignment }
set { titleLabel.textAlignment = newValue }
}
/// The font and size of the body label
@objc public dynamic var messageFont: UIFont {
get { return messageLabel.font }
set { messageLabel.font = newValue }
}
/// The color of the message label
@objc public dynamic var messageColor: UIColor? {
get { return messageLabel.textColor }
set { messageLabel.textColor = newValue}
}
/// The text alignment of the message label
@objc public dynamic var messageTextAlignment: NSTextAlignment {
get { return messageLabel.textAlignment }
set { messageLabel.textAlignment = newValue }
}
// MARK: - Views
/// The view that will contain the image, if set
internal lazy var imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
/// The title label of the dialog
internal lazy var titleLabel: UILabel = {
let titleLabel = UILabel(frame: .zero)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
titleLabel.textColor = UIColor(white: 0.4, alpha: 1)
titleLabel.font = .boldSystemFont(ofSize: 14)
return titleLabel
}()
/// The message label of the dialog
internal lazy var messageLabel: UILabel = {
let messageLabel = UILabel(frame: .zero)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.textColor = UIColor(white: 0.6, alpha: 1)
messageLabel.font = .systemFont(ofSize: 14)
return messageLabel
}()
/// The height constraint of the image view, 0 by default
internal var imageHeightConstraint: NSLayoutConstraint?
// MARK: - Initializers
internal override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View setup
internal func setupViews() {
// Self setup
translatesAutoresizingMaskIntoConstraints = false
// Add views
addSubview(imageView)
addSubview(titleLabel)
addSubview(messageLabel)
// Layout views
let views = ["imageView": imageView, "titleLabel": titleLabel, "messageLabel": messageLabel] as [String: Any]
var constraints = [NSLayoutConstraint]()
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[imageView]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-(==20@900)-[titleLabel]-(==20@900)-|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-(==20@900)-[messageLabel]-(==20@900)-|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[imageView]-(==30@900)-[titleLabel]-(==8@900)-[messageLabel]-(==30@900)-|", options: [], metrics: nil, views: views)
// ImageView height constraint
imageHeightConstraint = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 0, constant: 0)
if let imageHeightConstraint = imageHeightConstraint {
constraints.append(imageHeightConstraint)
}
// Activate constraints
NSLayoutConstraint.activate(constraints)
}
}
@@ -0,0 +1,133 @@
//
// PopupDialogDefaultViewController.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
final public class PopupDialogDefaultViewController: UIViewController {
public var standardView: PopupDialogDefaultView {
return view as! PopupDialogDefaultView // swiftlint:disable:this force_cast
}
override public func loadView() {
super.loadView()
view = PopupDialogDefaultView(frame: .zero)
}
}
public extension PopupDialogDefaultViewController {
// MARK: - Setter / Getter
// MARK: Content
/// The dialog image
var image: UIImage? {
get { return standardView.imageView.image }
set {
standardView.imageView.image = newValue
standardView.imageHeightConstraint?.constant = standardView.imageView.pv_heightForImageView()
}
}
/// The title text of the dialog
var titleText: String? {
get { return standardView.titleLabel.text }
set {
standardView.titleLabel.text = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The message text of the dialog
var messageText: String? {
get { return standardView.messageLabel.text }
set {
standardView.messageLabel.text = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
// MARK: Appearance
/// The font and size of the title label
@objc dynamic var titleFont: UIFont {
get { return standardView.titleFont }
set {
standardView.titleFont = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The color of the title label
@objc dynamic var titleColor: UIColor? {
get { return standardView.titleLabel.textColor }
set {
standardView.titleColor = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The text alignment of the title label
@objc dynamic var titleTextAlignment: NSTextAlignment {
get { return standardView.titleTextAlignment }
set {
standardView.titleTextAlignment = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The font and size of the body label
@objc dynamic var messageFont: UIFont {
get { return standardView.messageFont}
set {
standardView.messageFont = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The color of the message label
@objc dynamic var messageColor: UIColor? {
get { return standardView.messageColor }
set {
standardView.messageColor = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
/// The text alignment of the message label
@objc dynamic var messageTextAlignment: NSTextAlignment {
get { return standardView.messageTextAlignment }
set {
standardView.messageTextAlignment = newValue
standardView.pv_layoutIfNeededAnimated()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
standardView.imageHeightConstraint?.constant = standardView.imageView.pv_heightForImageView()
}
}
@@ -0,0 +1,127 @@
//
// PopupDialogOverlayView.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
/// The (blurred) overlay view below the popup dialog
final public class PopupDialogOverlayView: UIView {
// MARK: - Appearance
/// Turns the blur of the overlay view on or off
@objc public dynamic var blurEnabled: Bool {
get { return !blurView.isHidden }
set { blurView.isHidden = !newValue }
}
/// The blur radius of the overlay view
@objc public dynamic var blurRadius: CGFloat {
get { return blurView.blurRadius }
set { blurView.blurRadius = newValue }
}
/// Whether the blur view should allow for
/// live rendering of the background
@objc public dynamic var liveBlurEnabled: Bool {
get { return blurView.trackingMode == .common }
set {
if newValue {
blurView.trackingMode = .common
} else {
blurView.trackingMode = .none
}
}
}
/// The background color of the overlay view
@objc public dynamic var color: UIColor? {
get { return overlay.backgroundColor }
set { overlay.backgroundColor = newValue }
}
/// The opacity of the overlay view
@objc public dynamic var opacity: CGFloat {
get { return overlay.alpha }
set { overlay.alpha = newValue }
}
// MARK: - Views
internal lazy var blurView: DynamicBlurView = {
let blurView = DynamicBlurView(frame: .zero)
blurView.blurRadius = 8
blurView.trackingMode = .none
blurView.isDeepRendering = true
blurView.tintColor = .clear
blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
return blurView
}()
internal lazy var overlay: UIView = {
let overlay = UIView(frame: .zero)
overlay.backgroundColor = .black
overlay.alpha = 0.7
overlay.autoresizingMask = [.flexibleHeight, .flexibleWidth]
return overlay
}()
// MARK: - Inititalizers
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View setup
fileprivate func setupView() {
autoresizingMask = [.flexibleHeight, .flexibleWidth]
backgroundColor = .clear
alpha = 0
addSubview(blurView)
addSubview(overlay)
}
}
// MARK: - Deprecated
extension PopupDialogOverlayView {
/// Whether the blur view should allow for
/// dynamic rendering of the background
@available(*, deprecated, message: "liveBlur has been deprecated and will be removed with future versions of PopupDialog. Please use isLiveBlurEnabled instead.")
@objc public dynamic var liveBlur: Bool {
get { return liveBlurEnabled }
set { liveBlurEnabled = newValue }
}
}
@@ -0,0 +1,61 @@
//
// PopupDialogPresentationController.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
final internal class PresentationController: UIPresentationController {
private lazy var overlay: PopupDialogOverlayView = {
return PopupDialogOverlayView(frame: .zero)
}()
override func presentationTransitionWillBegin() {
guard let containerView = containerView else { return }
overlay.frame = containerView.bounds
containerView.insertSubview(overlay, at: 0)
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [weak self] _ in
self?.overlay.alpha = 1.0
}, completion: nil)
}
override func dismissalTransitionWillBegin() {
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [weak self] _ in
self?.overlay.alpha = 0.0
}, completion: nil)
}
override func containerViewWillLayoutSubviews() {
guard let presentedView = presentedView else { return }
presentedView.frame = frameOfPresentedViewInContainerView
overlay.blurView.refresh()
}
}
@@ -0,0 +1,86 @@
//
// PopupDialogPresentationManager.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
final internal class PresentationManager: NSObject, UIViewControllerTransitioningDelegate {
var transitionStyle: PopupDialogTransitionStyle
var interactor: InteractiveTransition
init(transitionStyle: PopupDialogTransitionStyle, interactor: InteractiveTransition) {
self.transitionStyle = transitionStyle
self.interactor = interactor
super.init()
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let presentationController = PresentationController(presentedViewController: presented, presenting: source)
return presentationController
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
var transition: TransitionAnimator
switch transitionStyle {
case .bounceUp:
transition = BounceUpTransition(direction: .in)
case .bounceDown:
transition = BounceDownTransition(direction: .in)
case .zoomIn:
transition = ZoomTransition(direction: .in)
case .fadeIn:
transition = FadeTransition(direction: .in)
}
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if interactor.hasStarted || interactor.shouldFinish {
return DismissInteractiveTransition()
}
var transition: TransitionAnimator
switch transitionStyle {
case .bounceUp:
transition = BounceUpTransition(direction: .out)
case .bounceDown:
transition = BounceDownTransition(direction: .out)
case .zoomIn:
transition = ZoomTransition(direction: .out)
case .fadeIn:
transition = FadeTransition(direction: .out)
}
return transition
}
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil
}
}
@@ -0,0 +1,186 @@
//
// PopupDialogTransitionAnimations.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/*!
Presentation transition styles for the popup dialog
- BounceUp: Dialog bounces in from bottom and is dismissed to bottom
- BounceDown: Dialog bounces in from top and is dismissed to top
- ZoomIn: Dialog zooms in and is dismissed by zooming out
- FadeIn: Dialog fades in and is dismissed by fading out
*/
@objc public enum PopupDialogTransitionStyle: Int {
case bounceUp
case bounceDown
case zoomIn
case fadeIn
}
/// Dialog bounces in from bottom and is dismissed to bottom
final internal class BounceUpTransition: TransitionAnimator {
init(direction: AnimationDirection) {
super.init(inDuration: 0.22, outDuration: 0.2, direction: direction)
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
switch direction {
case .in:
to.view.bounds.origin = CGPoint(x: 0, y: -from.view.bounds.size.height)
UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: [.curveEaseOut], animations: { [weak self] in
guard let self = self else { return }
self.to.view.bounds = self.from.view.bounds
}, completion: { _ in
transitionContext.completeTransition(true)
})
case .out:
UIView.animate(withDuration: outDuration, delay: 0.0, options: [.curveEaseIn], animations: { [weak self] in
guard let self = self else { return }
self.from.view.bounds.origin = CGPoint(x: 0, y: -self.from.view.bounds.size.height)
self.from.view.alpha = 0.0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}
/// Dialog bounces in from top and is dismissed to top
final internal class BounceDownTransition: TransitionAnimator {
init(direction: AnimationDirection) {
super.init(inDuration: 0.22, outDuration: 0.2, direction: direction)
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
switch direction {
case .in:
to.view.bounds.origin = CGPoint(x: 0, y: from.view.bounds.size.height)
UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: [.curveEaseOut], animations: { [weak self] in
guard let self = self else { return }
self.to.view.bounds = self.from.view.bounds
}, completion: { _ in
transitionContext.completeTransition(true)
})
case .out:
UIView.animate(withDuration: outDuration, delay: 0.0, options: [.curveEaseIn], animations: { [weak self] in
guard let self = self else { return }
self.from.view.bounds.origin = CGPoint(x: 0, y: self.from.view.bounds.size.height)
self.from.view.alpha = 0.0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}
/// Dialog zooms in and is dismissed by zooming out
final internal class ZoomTransition: TransitionAnimator {
init(direction: AnimationDirection) {
super.init(inDuration: 0.22, outDuration: 0.2, direction: direction)
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
switch direction {
case .in:
to.view.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: [.curveEaseOut], animations: { [weak self] in
guard let self = self else { return }
self.to.view.transform = CGAffineTransform(scaleX: 1, y: 1)
}, completion: { _ in
transitionContext.completeTransition(true)
})
case .out:
UIView.animate(withDuration: outDuration, delay: 0.0, options: [.curveEaseIn], animations: { [weak self] in
guard let self = self else { return }
self.from.view.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
self.from.view.alpha = 0.0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}
/// Dialog fades in and is dismissed by fading out
final internal class FadeTransition: TransitionAnimator {
init(direction: AnimationDirection) {
super.init(inDuration: 0.22, outDuration: 0.2, direction: direction)
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
switch direction {
case .in:
to.view.alpha = 0
UIView.animate(withDuration: 0.6, delay: 0.0, options: [.curveEaseOut],
animations: { [weak self] in
guard let self = self else { return }
self.to.view.alpha = 1
}, completion: { _ in
transitionContext.completeTransition(true)
})
case .out:
UIView.animate(withDuration: outDuration, delay: 0.0, options: [.curveEaseIn], animations: { [weak self] in
guard let self = self else { return }
self.from.view.alpha = 0.0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}
/// Used for the always drop out animation with pan gesture dismissal
final internal class DismissInteractiveTransition: TransitionAnimator {
init() {
super.init(inDuration: 0.22, outDuration: 0.32, direction: .out)
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
UIView.animate(withDuration: outDuration, delay: 0.0, options: [.beginFromCurrentState], animations: { [weak self] in
guard let self = self else { return }
self.from.view.bounds.origin = CGPoint(x: 0, y: -self.from.view.bounds.size.height)
self.from.view.alpha = 0.0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
@@ -0,0 +1,68 @@
//
// PopupDialogTransitionAnimator.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/// Base class for custom transition animations
internal class TransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
var to: UIViewController!
var from: UIViewController!
let inDuration: TimeInterval
let outDuration: TimeInterval
let direction: AnimationDirection
init(inDuration: TimeInterval, outDuration: TimeInterval, direction: AnimationDirection) {
self.inDuration = inDuration
self.outDuration = outDuration
self.direction = direction
super.init()
}
internal func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return direction == .in ? inDuration : outDuration
}
internal func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch direction {
case .in:
guard let to = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
let from = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
self.to = to
self.from = from
let container = transitionContext.containerView
container.addSubview(to.view)
case .out:
guard let to = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
let from = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
self.to = to
self.from = from
}
}
}
@@ -0,0 +1,44 @@
//
// UIImageView+Calculations.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
internal extension UIImageView {
/*!
Calculates the height of the the UIImageView has to
have so the image is displayed correctly
- returns: Height to set on the imageView
*/
func pv_heightForImageView() -> CGFloat {
guard let image = image, image.size.height > 0 else {
return 0.0
}
let width = bounds.size.width
let ratio = image.size.height / image.size.width
return width * ratio
}
}
@@ -0,0 +1,82 @@
//
// UIView+Animations.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
/*!
The intended direction of the animation
- in: Animate in
- out: Animate out
*/
internal enum AnimationDirection {
case `in` // swiftlint:disable:this identifier_name
case out
}
internal extension UIView {
/// The key for the fade animation
var fadeKey: String { return "FadeAnimation" }
var shakeKey: String { return "ShakeAnimation" }
func pv_fade(_ direction: AnimationDirection, _ value: Float, duration: CFTimeInterval = 0.08) {
layer.removeAnimation(forKey: fadeKey)
let animation = CABasicAnimation(keyPath: "opacity")
animation.duration = duration
animation.fromValue = layer.presentation()?.opacity
layer.opacity = value
animation.fillMode = CAMediaTimingFillMode.forwards
layer.add(animation, forKey: fadeKey)
}
func pv_layoutIfNeededAnimated(duration: CFTimeInterval = 0.08) {
UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(), animations: {
self.layoutIfNeeded()
}, completion: nil)
}
// As found at https://gist.github.com/mourad-brahim/cf0bfe9bec5f33a6ea66#file-uiview-animations-swift-L9
// Slightly modified
func pv_shake() {
layer.removeAnimation(forKey: shakeKey)
let vals: [Double] = [-2, 2, -2, 2, 0]
let translation = CAKeyframeAnimation(keyPath: "transform.translation.x")
translation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
translation.values = vals
let rotation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
rotation.values = vals.map { (degrees: Double) in
let radians: Double = (Double.pi * degrees) / 180.0
return radians
}
let shakeGroup: CAAnimationGroup = CAAnimationGroup()
shakeGroup.animations = [translation, rotation]
shakeGroup.duration = 0.3
self.layer.add(shakeGroup, forKey: shakeKey)
}
}
@@ -0,0 +1,52 @@
//
// UIViewController+Visibility.swift
//
// Copyright (c) 2016 Orderella Ltd. (http://orderella.co.uk)
// Author - Martin Wildfeuer (http://www.mwfire.de)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import UIKit
// http://stackoverflow.com/questions/2777438/how-to-tell-if-uiviewcontrollers-view-is-visible
internal extension UIViewController {
var isTopAndVisible: Bool {
return isVisible && isTopViewController
}
var isVisible: Bool {
if isViewLoaded {
return view.window != nil
}
return false
}
var isTopViewController: Bool {
if self.navigationController != nil {
return self.navigationController?.visibleViewController === self
} else if self.tabBarController != nil {
return self.tabBarController?.selectedViewController == self && self.presentedViewController == nil
} else {
return self.presentedViewController == nil && self.isVisible
}
}
}
@@ -0,0 +1,41 @@
//
// ScrollRects.swift
// TableViewDragger
//
// Created by Kyohei Ito on 2017/12/08.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import Foundation
import CoreImage
struct ScrollRects {
private let maxDistance: CGFloat = 10
private let size: CGSize
private let scrollRange: CGFloat
let topRect: CGRect
let bottomRect: CGRect
init(size: CGSize) {
self.size = size
scrollRange = size.height / 2.5
let scrollSize = CGSize(width: size.width, height: scrollRange)
topRect = CGRect(origin: .zero, size: scrollSize)
bottomRect = CGRect(origin: CGPoint(x: 0, y: size.height - scrollRange), size: scrollSize)
}
func distance(at point: CGPoint) -> CGFloat {
let ratio: CGFloat
if topRect.contains(point) {
ratio = -(scrollRange - point.y)
} else if bottomRect.contains(point) {
ratio = point.y - (size.height - scrollRange)
} else {
ratio = 0
}
return max(min(ratio / 30, maxDistance), -maxDistance)
}
}
@@ -0,0 +1,371 @@
//
// TableViewDragger.swift
// TableViewDragger
//
// Created by Kyohei Ito on 2015/09/24.
// Copyright © 2015 kyohei_ito. All rights reserved.
//
import UIKit
@objc public protocol TableViewDraggerDelegate: class {
/// If allow movement of cell, please return `true`. require a call to `moveRowAt:toIndexPath:` of UITableView and rearranged of data.
func dragger(_ dragger: TableViewDragger, moveDraggingAt indexPath: IndexPath, newIndexPath: IndexPath) -> Bool
/// If allow dragging of cell, prease return `true`.
@objc optional func dragger(_ dragger: TableViewDragger, shouldDragAt indexPath: IndexPath) -> Bool
@objc optional func dragger(_ dragger: TableViewDragger, willBeginDraggingAt indexPath: IndexPath)
@objc optional func dragger(_ dragger: TableViewDragger, didBeginDraggingAt indexPath: IndexPath)
@objc optional func dragger(_ dragger: TableViewDragger, willEndDraggingAt indexPath: IndexPath)
@objc optional func dragger(_ dragger: TableViewDragger, didEndDraggingAt indexPath: IndexPath)
}
@objc public protocol TableViewDraggerDataSource: class {
/// Return any cell if want to change the cell in drag.
@objc optional func dragger(_ dragger: TableViewDragger, cellForRowAt indexPath: IndexPath) -> UIView?
/// Return the indexPath if want to change the indexPath to start drag.
@objc optional func dragger(_ dragger: TableViewDragger, indexPathForDragAt indexPath: IndexPath) -> IndexPath
}
open class TableViewDragger: NSObject {
let longPressGesture = UILongPressGestureRecognizer()
let panGesture = UIPanGestureRecognizer()
var draggingCell: TableViewDraggerCell?
var displayLink: CADisplayLink?
var targetClipsToBounds = true
weak var targetTableView: UITableView?
private var draggingDirection: UIScrollView.DraggingDirection?
/// It will be `true` if want to hide the original cell.
open var isHiddenOriginCell: Bool = true
/// Zoom scale of cell in drag.
open var zoomScaleForCell: CGFloat = 1
/// Alpha of cell in drag.
open var alphaForCell: CGFloat = 1
/// Opacity of cell shadow in drag.
open var opacityForShadowOfCell: Float = 0.4
/// Velocity of auto scroll in drag.
open var scrollVelocity: CGFloat = 1
open weak var delegate: TableViewDraggerDelegate?
open weak var dataSource: TableViewDraggerDataSource?
//
open var availableHorizontalScroll : Bool = true
open var tableView: UITableView? {
return targetTableView
}
/// `UITableView` want to drag.
public init(tableView: UITableView, _ minimumPressDuration: CFTimeInterval = 0.5) {
super.init()
self.targetTableView = tableView
tableView.addGestureRecognizer(longPressGesture)
tableView.addGestureRecognizer(panGesture)
longPressGesture.addTarget(self, action: #selector(TableViewDragger.longPressGestureAction(_:)))
longPressGesture.delegate = self
longPressGesture.allowableMovement = 5.0
longPressGesture.minimumPressDuration = minimumPressDuration
panGesture.addTarget(self, action: #selector(TableViewDragger.panGestureAction(_:)))
panGesture.delegate = self
panGesture.maximumNumberOfTouches = 1
}
deinit {
targetTableView?.removeGestureRecognizer(longPressGesture)
targetTableView?.removeGestureRecognizer(panGesture)
}
func targetIndexPath(_ tableView: UITableView, draggingCell: TableViewDraggerCell) -> IndexPath {
let location = draggingCell.location
let offsetY = (draggingCell.viewHeight / 2) + 2
let offsetX = tableView.center.x
let topPoint = CGPoint(x: offsetX, y: location.y - offsetY)
let bottomPoint = CGPoint(x: offsetX, y: location.y + offsetY)
let point = draggingDirection == .up ? topPoint : bottomPoint
if let targetIndexPath = tableView.indexPathForRow(at: point) {
if tableView.cellForRow(at: targetIndexPath) == nil {
return draggingCell.dropIndexPath
}
let targetRect = tableView.rectForRow(at: targetIndexPath)
let targetCenterY = targetRect.origin.y + (targetRect.height / 2)
guard let direction = draggingDirection else {
return draggingCell.dropIndexPath
}
switch direction {
case .up:
if (targetCenterY > point.y && draggingCell.dropIndexPath > targetIndexPath) {
return targetIndexPath
}
case .down:
if (targetCenterY < point.y && draggingCell.dropIndexPath < targetIndexPath) {
return targetIndexPath
}
}
} else {
let section = (0..<tableView.numberOfSections).filter { section -> Bool in
tableView.rect(forSection: section).contains(point)
}.first
if let section = section, tableView.numberOfRows(inSection: section) == 0 {
return IndexPath(row: 0, section: section)
}
}
return draggingCell.dropIndexPath
}
func dragCell(_ tableView: UITableView, draggingCell: TableViewDraggerCell) {
let indexPath = targetIndexPath(tableView, draggingCell: draggingCell)
if draggingCell.dropIndexPath.compare(indexPath) == .orderedSame {
return
}
if let cell = tableView.cellForRow(at: draggingCell.dropIndexPath) {
cell.isHidden = isHiddenOriginCell
}
if delegate?.dragger(self, moveDraggingAt: draggingCell.dropIndexPath, newIndexPath: indexPath) == true {
draggingCell.dropIndexPath = indexPath
}
}
func copiedCell(at indexPath: IndexPath) -> UIView? {
if let view = dataSource?.dragger?(self, cellForRowAt: indexPath) {
return view
}
if let cell = targetTableView?.cellForRow(at: indexPath) {
if let view = cell.snapshotView(afterScreenUpdates: false) {
return view
} else if let view = cell.captured() {
view.frame = cell.bounds
return view
}
}
return nil
}
func draggedCell(_ tableView: UITableView, indexPath: IndexPath) -> TableViewDraggerCell? {
guard let copiedCell = copiedCell(at: indexPath) else {
return nil
}
let cellRect = tableView.rectForRow(at: indexPath)
copiedCell.frame.size = cellRect.size
if let height = tableView.delegate?.tableView?(tableView, heightForRowAt: indexPath) {
copiedCell.frame.size.height = height
}
let cell = TableViewDraggerCell(cell: copiedCell)
cell.dragScale = zoomScaleForCell
cell.dragAlpha = alphaForCell
cell.dragShadowOpacity = opacityForShadowOfCell
cell.dropIndexPath = indexPath
return cell
}
}
// MARK: - Dragging Cell
extension TableViewDragger {
private func draggingDidBegin(_ gesture: UIGestureRecognizer, indexPath: IndexPath) {
displayLink?.invalidate()
displayLink = UIScreen.main.displayLink(withTarget: self, selector: #selector(TableViewDragger.displayDidRefresh(_:)))
displayLink?.add(to: .main, forMode: .default)
displayLink?.isPaused = true
let dragIndexPath = dataSource?.dragger?(self, indexPathForDragAt: indexPath) ?? indexPath
delegate?.dragger?(self, willBeginDraggingAt: dragIndexPath)
if let tableView = targetTableView {
let actualCell = tableView.cellForRow(at: dragIndexPath)
if let draggedCell = draggedCell(tableView, indexPath: dragIndexPath) {
let point = gesture.location(in: actualCell)
if availableHorizontalScroll == true{
draggedCell.offset = point
draggedCell.transformToPoint(point)
draggedCell.location = gesture.location(in: tableView)
} else {
draggedCell.offset = CGPoint(x: (draggedCell.frame.size.width)/2, y: point.y)
draggedCell.transformToPoint(CGPoint(x: (draggedCell.frame.size.width)/2, y: point.y))
draggedCell.location = CGPoint(x: (draggedCell.frame.size.width)/2, y: gesture.location(in: tableView).y)
}
tableView.addSubview(draggedCell)
draggingCell = draggedCell
}
actualCell?.isHidden = isHiddenOriginCell
targetClipsToBounds = tableView.clipsToBounds
tableView.clipsToBounds = false
}
delegate?.dragger?(self, didBeginDraggingAt: indexPath)
}
private func draggingDidChange(_ gesture: UIGestureRecognizer, direction: UIScrollView.DraggingDirection?) {
guard let tableView = targetTableView, let draggingCell = draggingCell else {
return
}
if availableHorizontalScroll == true{
draggingCell.location = gesture.location(in: tableView)
} else {
draggingCell.location = CGPoint(x: (draggingCell.frame.size.width)/2, y: gesture.location(in: tableView).y)
}
if let adjustedDirection = tableView.draggingDirection(at: draggingCell.adjustedCenter(on: tableView)) {
displayLink?.isPaused = false
draggingDirection = adjustedDirection
} else {
draggingDirection = direction
}
dragCell(tableView, draggingCell: draggingCell)
}
private func draggingDidEnd(_ gesture: UIGestureRecognizer) {
displayLink?.invalidate()
displayLink = nil
guard let tableView = targetTableView, let draggingCell = draggingCell else {
return
}
delegate?.dragger?(self, willEndDraggingAt: draggingCell.dropIndexPath)
let targetRect = tableView.rectForRow(at: draggingCell.dropIndexPath)
let center = CGPoint(x: targetRect.width / 2, y: targetRect.origin.y + (targetRect.height / 2))
draggingCell.drop(center) {
self.delegate?.dragger?(self, didEndDraggingAt: draggingCell.dropIndexPath)
if let cell = tableView.cellForRow(at: draggingCell.dropIndexPath) {
cell.isHidden = false
}
tableView.clipsToBounds = self.targetClipsToBounds
self.draggingCell = nil
}
}
}
// MARK: - Action Methods
private extension TableViewDragger {
@objc func displayDidRefresh(_ displayLink: CADisplayLink) {
guard let tableView = targetTableView, let draggingCell = draggingCell else {
return
}
let center = draggingCell.adjustedCenter(on: tableView)
if let direction = tableView.draggingDirection(at: center) {
draggingDirection = direction
} else {
displayLink.isPaused = true
}
tableView.contentOffset = tableView.preferredContentOffset(at: center, velocity: scrollVelocity)
dragCell(tableView, draggingCell: draggingCell)
if availableHorizontalScroll == true{
draggingCell.location = panGesture.location(in: tableView)
} else {
draggingCell.location = CGPoint(x: draggingCell.frame.size.width/2, y: panGesture.location(in: tableView).y)
}
}
@objc func longPressGestureAction(_ gesture: UILongPressGestureRecognizer) {
switch gesture.state {
case .began:
targetTableView?.isScrollEnabled = false
let point = gesture.location(in: targetTableView)
if let path = targetTableView?.indexPathForRow(at: point) {
draggingDidBegin(gesture, indexPath: path)
}
case .ended, .cancelled:
draggingDidEnd(gesture)
targetTableView?.isScrollEnabled = true
case .changed, .failed, .possible:
break
@unknown default:
return
}
}
@objc func panGestureAction(_ gesture: UIPanGestureRecognizer) {
guard targetTableView?.isScrollEnabled == false && gesture.state == .changed else {
return
}
let offsetY = gesture.translation(in: targetTableView).y
if offsetY < 0 {
draggingDidChange(gesture, direction: .up)
} else if offsetY > 0 {
draggingDidChange(gesture, direction: .down)
} else {
draggingDidChange(gesture, direction: nil)
}
gesture.setTranslation(.zero, in: targetTableView)
}
}
// MARK: - UIGestureRecognizerDelegate Methods
extension TableViewDragger: UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if gestureRecognizer == longPressGesture {
let point = touch.location(in: targetTableView)
if let indexPath = targetTableView?.indexPathForRow(at: point) {
if let ret = delegate?.dragger?(self, shouldDragAt: indexPath) {
return ret
}
} else {
return false
}
}
return true
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return gestureRecognizer == panGesture || otherGestureRecognizer == panGesture || gestureRecognizer == longPressGesture || otherGestureRecognizer == longPressGesture
}
}
extension UIView {
fileprivate func captured() -> UIView? {
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) else {
return nil
}
return try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [UIView.self], from: data) as? UIView
}
}
@@ -0,0 +1,136 @@
//
// TableViewDraggerCell.swift
// TableViewDragger
//
// Created by Kyohei Ito on 2015/09/24.
// Copyright © 2015 kyohei_ito. All rights reserved.
//
import UIKit
class TableViewDraggerCell: UIScrollView {
private let zoomingView: UIView!
var dragAlpha: CGFloat = 1
var dragScale: CGFloat = 1
var dragShadowOpacity: Float = 0.4
var dropIndexPath: IndexPath = IndexPath(index: 0)
var offset: CGPoint = CGPoint.zero {
didSet {
offset.x -= (bounds.width / 2)
offset.y -= (bounds.height / 2)
center = adjustCenter(location)
}
}
var location: CGPoint = CGPoint.zero {
didSet {
center = adjustCenter(location)
}
}
var viewHeight: CGFloat {
return zoomingView.bounds.height * zoomScale
}
private func adjustCenter(_ center: CGPoint) -> CGPoint {
var center = center
center.x -= offset.x
center.y -= offset.y
return center
}
required init?(coder aDecoder: NSCoder) {
zoomingView = UIView(frame: .zero)
super.init(coder: aDecoder)
}
init(cell: UIView) {
zoomingView = UIView(frame: cell.bounds)
zoomingView.addSubview(cell)
super.init(frame: cell.bounds)
delegate = self
clipsToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0
layer.shadowRadius = 5
layer.shadowOffset = .zero
addSubview(zoomingView)
}
func transformToPoint(_ point: CGPoint) {
if dragScale > 1 {
maximumZoomScale = dragScale
} else {
minimumZoomScale = dragScale
}
var center = zoomingView.center
center.x -= (center.x * dragScale) - point.x
center.y -= (center.y * dragScale) - point.y
UIView.animate(withDuration: 0.25, delay: 0.1, options: .curveEaseInOut, animations: {
self.zoomingView.center = center
self.zoomScale = self.dragScale
self.alpha = self.dragAlpha
}, completion: nil)
CATransaction.begin()
let anim = CABasicAnimation(keyPath: "shadowOpacity")
anim.fromValue = 0
anim.toValue = dragShadowOpacity
anim.duration = 0.1
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
layer.add(anim, forKey: "cellDragAnimation")
CATransaction.commit()
}
func adjustedCenter(on scrollView: UIScrollView) -> CGPoint {
var center = location
center.y -= scrollView.contentOffset.y
center.x = scrollView.center.x
return center
}
func drop(_ center: CGPoint, completion: (() -> Void)? = nil) {
UIView.animate(withDuration: 0.25) {
self.zoomingView.adjustCenterAtRect(self.zoomingView.frame)
self.center = center
self.zoomScale = 1.0
self.alpha = 1.0
}
CATransaction.begin()
let anim = CABasicAnimation(keyPath: "shadowOpacity")
anim.fromValue = dragShadowOpacity
anim.toValue = 0
anim.duration = 0.15
anim.beginTime = CACurrentMediaTime() + 0.15
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
CATransaction.setCompletionBlock {
self.removeFromSuperview()
completion?()
}
layer.add(anim, forKey: "cellDropAnimation")
CATransaction.commit()
}
}
extension TableViewDraggerCell: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return zoomingView
}
}
private extension UIView {
func adjustCenterAtRect(_ rect: CGRect) {
let center = CGPoint(x: rect.size.width / 2, y: rect.size.height / 2)
self.center = center
}
}
@@ -0,0 +1,58 @@
//
// UIScrollViewExtension.swift
// Pods
//
// Created by Kyohei Ito on 2015/09/29.
//
//
import UIKit
extension UIScrollView {
enum DraggingDirection {
case up
case down
}
func preferredContentOffset(at point: CGPoint, velocity: CGFloat) -> CGPoint {
let distance = ScrollRects(size: bounds.size).distance(at: point) / velocity
var offset = contentOffset
offset.y += distance
let topOffset = -contentInset.top
let bottomOffset = contentInset.bottom
let height = floor(contentSize.height) - bounds.size.height
if offset.y > height + bottomOffset {
offset.y = height + bottomOffset
} else if offset.y < topOffset {
offset.y = topOffset
}
return offset
}
func draggingDirection(at point: @autoclosure () -> CGPoint) -> DraggingDirection? {
let contentHeight = floor(contentSize.height)
if bounds.size.height >= contentHeight {
return nil
}
let rects = ScrollRects(size: bounds.size)
let point = point()
if rects.topRect.contains(point) {
let topOffset = -contentInset.top
if contentOffset.y > topOffset {
return .up
}
} else if rects.bottomRect.contains(point) {
let bottomOffset = contentHeight + contentInset.bottom - bounds.size.height
if contentOffset.y < bottomOffset {
return .down
}
}
return nil
}
}
@@ -0,0 +1,143 @@
//
// ViewController+MetalVideoProcessPlayerDelegate.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/9.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import Foundation
import MetalVideoProcess
import AVFoundation
import AVKit
extension ViewController: MetalVideoProcessPlayerDelegate {
func playbackFrameTimeChanged(frameTime time: CMTime, player: AVPlayer) {
DispatchQueue.main.async {
self.progress.value = Float(time.seconds)
}
}
func playEnded(currentPlayer player: AVPlayer) {
}
func finishExport(error: NSError?) {
DispatchQueue.main.async { [weak self] in
guard let `self` = self else { return }
if error != nil {
MBProgressHUD.hide(for: self.view, animated: true)
guard let source = self.movieWriter?.sources.sources[0] else {
return
}
source.removeAllTargets()
source --> self.renderView
self.view.isUserInteractionEnabled = true
let alertVC = UIAlertController(title: "", message: "导出失败, 请重新尝试", preferredStyle: .alert)
let alertBtn = UIAlertAction(title: "确认", style: .cancel) { (_) in
alertVC.dismiss(animated: true, completion: nil)
}
alertVC.addAction(alertBtn)
self.present(alertVC, animated: true) {
}
return
}
}
if error != nil {
self.view.isUserInteractionEnabled = true
return
}
//writer
self.movieWriter?.finishRecording({
//
DispatchQueue.main.async {
MBProgressHUD.hide(for: self.view, animated: true)
guard let source = self.movieWriter?.sources.sources[0] else {
return
}
source.removeAllTargets()
source --> self.renderView
let playerVC = AVPlayerViewController()
playerVC.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .done, target: self, action: #selector(self.showInfo))
playerVC.player = AVPlayer.init(url: self.movieWriter!.fileURL)
self.present(playerVC, animated: true, completion: nil)
self.view.isUserInteractionEnabled = true
}
})
}
func exportProgressChanged(_ progress: Float) {
DispatchQueue.main.async {
self.progressHUD?.progress = progress
}
}
@objc func showInfo() {
let data = NSData(contentsOf: self.movieWriter!.fileURL)
guard let size = data?.length else {
return
}
let mSize = Double(size) / (1024 * 1024)
let message = NSString(format: "%..2f MB", mSize) as String
let alertVC = UIAlertController(title: "Video file size", message: message, preferredStyle: .alert)
let alertBtn = UIAlertAction(title: "OK", style: .cancel) { (_) in
alertVC.dismiss(animated: true, completion: nil)
}
alertVC.addAction(alertBtn)
self.present(alertVC, animated: true) {
}
}
func initWriter() {
self.movieWriter?.removeSourceAtIndex(0)
self.movieWriter = nil
//
do {
let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
let fileURL = URL(string:"test.mp4", relativeTo:documentsDir)!
do {
try FileManager.default.removeItem(at:fileURL)
} catch {
}
let cgSize = MetalVideoProcessBackground.canvasSize
self.movieWriter = try MetalVideoProcessMovieWriter(URL: fileURL,
size: Size(width: Float(cgSize.width),
height: Float(cgSize.height)),
liveVideo: false)
self.player?.audioEncodingTarget = nil
self.player?.audioEncodingTarget = self.movieWriter
//writer
} catch {
}
}
@IBAction func export(_ sender: Any) {
self.progressHUD = MBProgressHUD.showHub(with: .loading("exporting..."), self.view, isUserInteractionEnabled: false)
guard let source = self.renderView?.sources.sources[0] else {
return
}
initWriter()
source.removeAllTargets()
source --> self.movieWriter!
self.view.isUserInteractionEnabled = false
try? self.player?.startExport()
self.movieWriter?.activateAudioTrack()
self.movieWriter?.startRecording()
}
}
@@ -0,0 +1,71 @@
//
// ViewController+UIGestureRecognizerDelegate.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/9.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import Foundation
import MetalVideoProcess
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true;
}
@IBAction func rotateAction(ges: UIRotationGestureRecognizer) {
guard let item = (self.mainSelectedItem != nil ? self.mainSelectedItem : self.subSelectedItem) else {
return
}
guard let transformFilter = item.transformFilter else {
return
}
let rotDeg = transformFilter.rotate + Double(-ges.velocity)
transformFilter.rotate = rotDeg
item.rotate = rotDeg
}
@IBAction func pinchAction(ges: UIPinchGestureRecognizer) {
guard let item = (self.mainSelectedItem != nil ? self.mainSelectedItem : self.subSelectedItem) else {
return
}
guard let transformFilter = item.transformFilter else {
return
}
transformFilter.scale = Position((transformFilter.scale.x) + Float(ges.velocity * 0.01), (transformFilter.scale.y) + Float(ges.velocity * 0.01))
var x: Float = (transformFilter.scale.x)
var y: Float = (transformFilter.scale.y)
if ((transformFilter.scale.x) < 0.01) {
x = 0.01
}
if ((transformFilter.scale.y) < 0.01) {
y = 0.01
}
transformFilter.scale = Position(x, y)
item.scale = transformFilter.scale
}
@IBAction func panAction(ges: UIPanGestureRecognizer) {
guard let item = (self.mainSelectedItem != nil ? self.mainSelectedItem : self.subSelectedItem) else {
return
}
guard let transformFilter = item.transformFilter else {
return
}
if ges.state == .changed {
let tanl = ges.translation(in: ges.view)
// have to content the view scaleFactor and the vertex to texture scale.
transformFilter.translation = Position(currentPostion.x + Float(tanl.x * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.width), currentPostion.y - Float(tanl.y * self.renderView.contentScaleFactor * 2.0) / Float(self.renderView.drawableSize.height))
} else if ges.state == .ended {
currentPostion = (transformFilter.translation)
}
item.translation = transformFilter.translation
}
}
@@ -0,0 +1,104 @@
//
// ViewController+UIImagePickerControllerDelegate.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/9.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import Foundation
import AVFoundation
import MobileCoreServices
func orientationForTrack(asset: AVAsset) -> UIInterfaceOrientation {
let track = asset.tracks(withMediaType: .video).first
guard let txf = track?.preferredTransform else {
return .portrait
}
if txf.a == 0 && txf.b == 1.0 && txf.c == -1.0 && txf.d == 0 {
return .landscapeRight
} else if txf.a == 0 && txf.b == -1.0 && txf.c == 1.0 && txf.d == 0 {
return .landscapeLeft
} else if txf.a == 1.0 && txf.b == 0 && txf.c == 0 && txf.d == 1.0 {
return .portrait
} else if txf.a == -1.0 && txf.b == 0 && txf.c == 0 && txf.d == -1.0 {
return .portraitUpsideDown
} else {
return .unknown
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
debugPrint("info:", info)
picker.dismiss(animated: true, completion: nil)
guard let mediaType = info[.mediaType] else {
return
}
if mediaType as! CFString == kUTTypeMovie {
debugPrint("video")
guard let url = info[.mediaURL] as? URL else {
return
}
let asset = AVAsset(url: url)
let orientation = orientationForTrack(asset: asset)
debugPrint("asset:", asset)
let item = ResourceItem(asset: asset)
item.fillType = .aspectToFill
item.orientation = orientation
switch item.orientation {
case .portrait:
break
case .landscapeLeft:
item.rotate = 90.0
break
case .landscapeRight:
item.rotate = -90.0
break
case .portraitUpsideDown:
item.rotate = 180.0
break
default:
break
}
if picker == self.subPicker {
try? self.videoEditor?.insertOverlayItem(overlayItem: item)
item.roi = CGRect(x: 0.4, y: 0.4, width: 0.8, height: 0.8)
self.subResources.append(item)
} else {
try? self.videoEditor?.insertItem(videoItem: item)
item.roi = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
self.mainResources.append(item)
}
self.rebuildPipeline()
self.subTableView.reloadData()
self.mainTableView.reloadData()
} else {
debugPrint("unsupported")
return
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
}
}
@@ -0,0 +1,108 @@
//
// ViewController+UITableViewDelegate.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/9.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import Foundation
import MetalVideoProcess
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//selected
if tableView == self.mainTableView {
let item = self.mainResources[indexPath.row]
self.mainSelectedItem = item
currentPostion = item.transformFilter?.translation ?? Position(0.0, 0.0)
self.mainEditButton.isEnabled = true
self.mainDeleteButton.isEnabled = true
self.subEditButton.isEnabled = false
self.subDeleteButton.isEnabled = false
self.subSelectedItem = nil
guard let subIndex = self.subTableView.indexPathForSelectedRow else {
return
}
self.subTableView.deselectRow(at: subIndex, animated: true)
} else {
let item = self.subResources[indexPath.row]
self.subSelectedItem = item
currentPostion = item.transformFilter?.translation ?? Position(0.0, 0.0)
self.mainEditButton.isEnabled = false
self.mainDeleteButton.isEnabled = false
self.subEditButton.isEnabled = true
self.subDeleteButton.isEnabled = true
self.mainSelectedItem = nil
guard let mainIndex = self.mainTableView.indexPathForSelectedRow else {
return
}
self.mainTableView.deselectRow(at: mainIndex, animated: true)
}
}
}
extension ViewController: TableViewDraggerDelegate {
func dragger(_ dragger: TableViewDragger, moveDraggingAt indexPath: IndexPath, newIndexPath: IndexPath) -> Bool {
if dragger == self.mainDragger! {
let item = self.mainResources[indexPath.row]
self.mainResources.remove(at: indexPath.row)
self.mainResources.insert(item, at: newIndexPath.row)
mainTableView.moveRow(at: indexPath, to: newIndexPath)
} else {
let item = self.subResources[indexPath.row]
self.subResources.remove(at: indexPath.row)
self.subResources.insert(item, at: newIndexPath.row)
subTableView.moveRow(at: indexPath, to: newIndexPath)
}
return true
}
func dragger(_ dragger: TableViewDragger, didEndDraggingAt indexPath: IndexPath) {
dragger.tableView?.reloadData()
}
func dragger(_ dragger: TableViewDragger, willEndDraggingAt indexPath: IndexPath) {
self.rebuildPipeline()
}
}
extension ViewController: TableViewDraggerDataSource {
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.mainTableView == tableView {
return self.mainResources.count
} else {
return self.subResources.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if self.mainTableView == tableView {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "mainCell", for: indexPath) as? ResourceItemTableViewCell else {
return UITableViewCell()
}
let item = self.mainResources[indexPath.row]
cell.textLabel?.text = NSString(format: "t:%d s:%@ d:%@", item.trackID, item.startTimeText, item.durationText) as String
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "pipCell", for: indexPath) as? ResourceItemTableViewCell else {
return UITableViewCell()
}
let item = self.subResources[indexPath.row]
cell.textLabel?.text = NSString(format: "t:%d s:%@ d:%@", item.trackID, item.startTimeText, item.durationText) as String
return cell
}
}
}
@@ -23,7 +23,17 @@ class ViewController: UIViewController {
var grayFilter: MetalVideoProcessLuminance?
public var transition: MetalVideoProcessTransition? = MetalVideoProcessFadeTransition()
deinit {
print(" ViewController deinit")
}
override func viewDidLoad() {
guard let transition = self.transition else {
return
}
super.viewDidLoad()
let asset1 = AVAsset(url: Bundle.main.url(forResource: "853", withExtension: "mp4")!)
let asset2 = AVAsset(url: Bundle.main.url(forResource: "cute", withExtension: "mp4")!)
@@ -34,6 +44,8 @@ class ViewController: UIViewController {
let transitionDuration = CMTime.init(seconds: 2.0, preferredTimescale: 600)
item1.videoTransition = TransitionDuration(duration: transitionDuration)
item1.audioTransition = FadeInOutAudioTransition(duration: transitionDuration)
do {
let editor = try MetalVideoEditor(videoItems: [item1, item2],
customVideoCompositorClass: MetalVideoProcessCompositor.self)
@@ -41,61 +53,38 @@ class ViewController: UIViewController {
let playerItem = editor.buildPlayerItem()
self.progress.maximumValue = Float(playerItem.duration.seconds)
let player = try MetalVideoProcessPlayer(playerItem: playerItem)
let beautyFilter1 = MetalVideoProcessBeautyFilter()
beautyFilter1.saveUniformSettings(forTimelineRange: item1.timeRange, trackID: item1.trackID)
beautyFilter1.isEnable = false
let beautyFilter2 = MetalVideoProcessBeautyFilter()
beautyFilter2.saveUniformSettings(forTimelineRange: item2.timeRange, trackID: item2.trackID)
beautyFilter2.isEnable = false
let blurFilter1 = MetalVideoProcessGaussianBlurFilter()
blurFilter1.saveUniformSettings(forTimelineRange: item1.timeRange, trackID: item1.trackID)
blurFilter1.isEnable = false
let blurFilter2 = MetalVideoProcessGaussianBlurFilter()
blurFilter2.saveUniformSettings(forTimelineRange: item2.timeRange, trackID: item2.trackID)
blurFilter2.isEnable = false
let gray = MetalVideoProcessLuminance()
gray.isEnable = false
self.grayFilter = gray
self.beauty1 = beautyFilter1
self.beauty2 = beautyFilter2
self.blur1 = blurFilter1
self.blur2 = blurFilter2
let transitionTimeRange = item1.timeRange.intersection(item2.timeRange)
let fadeTransition = MetalVideoProcessFadeTransition()
//
fadeTransition.mainTrackIDs.append(item1.trackID)
fadeTransition.mainTrackIDs.append(item2.trackID)
transition.mainTrackIDs.append(item1.trackID)
transition.mainTrackIDs.append(item2.trackID)
// item1item2intersection
fadeTransition.saveUniformSettings(forTimelineRange: transitionTimeRange, trackID: 0)
item1.transitoin = fadeTransition
transition.saveUniformSettings(forTimelineRange: transitionTimeRange, trackID: 0)
item1.transitoin = transition
//Begin build pipeline
player.addTarget(beautyFilter1, atTargetIndex: nil, trackID: item1.trackID, targetTrackId: 0)
player.addTarget(beautyFilter2, atTargetIndex: nil, trackID: item2.trackID, targetTrackId: item2.trackID)
//mapping trackId on mainTrack 0
beautyFilter1 --> blurFilter1 --> fadeTransition
beautyFilter2 --> blurFilter2 --> fadeTransition --> gray --> renderView
//Done
player.addTarget(transition, atTargetIndex: nil, trackID: item1.trackID, targetTrackId: 0)
player.addTarget(transition, atTargetIndex: nil, trackID: item2.trackID, targetTrackId: item2.trackID)
transition --> renderView
self.player = player
self.player?.playerDelegate = self
self.player?.play()
} catch {
debugPrint("init error")
}
}
override func viewDidDisappear(_ animated: Bool) {
// player?.suspend()
player?.dispose()
// player?.removeAllTargets()
// player = nil
}
@IBAction func play(_ sender: Any) {
self.player?.play()
}
+258 -42
View File
@@ -7,9 +7,19 @@
objects = {
/* Begin PBXBuildFile section */
6A24CA0724ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24C9FB24ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.metal */; };
6A24CA0824ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24C9FC24ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.metal */; };
6A24CA0924ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24C9FD24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.metal */; };
6A24CA0A24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24C9FE24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.swift */; };
6A24CA0B24ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24C9FF24ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.swift */; };
6A24CA0C24ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0024ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.metal */; };
6A24CA0D24ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0124ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.swift */; };
6A24CA0E24ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0224ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.swift */; };
6A24CA0F24ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0324ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.swift */; };
6A24CA1024ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0424ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.metal */; };
6A24CA1124ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0524ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.swift */; };
6A24CA1224ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A24CA0624ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.metal */; };
6A3E705924BD9099009A0AC9 /* MetalVideoProcessTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3E705824BD9099009A0AC9 /* MetalVideoProcessTransition.swift */; };
6A3E708824C0448B009A0AC9 /* MetalVideoProcessFadeTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3E708724C0448B009A0AC9 /* MetalVideoProcessFadeTransition.swift */; };
6A3E708A24C044A0009A0AC9 /* MetalVideoProcessFadeTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A3E708924C044A0009A0AC9 /* MetalVideoProcessFadeTransition.metal */; };
6A874BC224C594EC00ADFD8A /* MetalVideoProcessMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A874BC124C594EC00ADFD8A /* MetalVideoProcessMotion.swift */; };
6A874BD024C6E1C200ADFD8A /* MetalVideoProcessFadeInMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A874BCF24C6E1C200ADFD8A /* MetalVideoProcessFadeInMotion.swift */; };
6A874BD424C6F98100ADFD8A /* MetalVideoProcessFadeMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6A874BD324C6F98100ADFD8A /* MetalVideoProcessFadeMotion.metal */; };
@@ -87,20 +97,74 @@
6ADB072A24ADCC250010A817 /* MetalVideoProcessColorFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB072724ADCC250010A817 /* MetalVideoProcessColorFilter.swift */; };
6ADB072B24ADCC250010A817 /* MetalVideoProcessColorFilter.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB072824ADCC250010A817 /* MetalVideoProcessColorFilter.metal */; };
6ADB07E124AEDC9B0010A817 /* default.metal in Sources */ = {isa = PBXBuildFile; fileRef = 6ADB07E024AEDC9B0010A817 /* default.metal */; };
AB17C21024C17D770023C974 /* MetalVideoProcessEraseDownTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB17C20E24C17D770023C974 /* MetalVideoProcessEraseDownTransition.swift */; };
AB17C21124C17D770023C974 /* MetalVideoProcessEraseDownTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB17C20F24C17D770023C974 /* MetalVideoProcessEraseDownTransition.metal */; };
AB17C21424C182780023C974 /* MetalVideoProcessMirrorRotateTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB17C21224C182780023C974 /* MetalVideoProcessMirrorRotateTransition.metal */; };
AB17C21524C182780023C974 /* MetalVideoProcessMirrorRotateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB17C21324C182780023C974 /* MetalVideoProcessMirrorRotateTransition.swift */; };
AB17C21824C5737B0023C974 /* MetalVideoProcessReflectTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB17C21624C5737B0023C974 /* MetalVideoProcessReflectTransition.metal */; };
AB17C21924C5737B0023C974 /* MetalVideoProcessReflectTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB17C21724C5737B0023C974 /* MetalVideoProcessReflectTransition.swift */; };
AB64836224C03E6200A0266B /* MetalVideoProcessShanBaiTrans.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB64836024C03E6200A0266B /* MetalVideoProcessShanBaiTrans.metal */; };
AB64836324C03E6200A0266B /* MetalVideoProcessShanBaiTrans.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB64836124C03E6200A0266B /* MetalVideoProcessShanBaiTrans.swift */; };
AB00F87324D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87124D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.metal */; };
AB00F87424D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87224D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.swift */; };
AB00F87724D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87524D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.metal */; };
AB00F87824D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87624D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.swift */; };
AB00F87B24D7F45E00413EE8 /* MetalVideoProcessUpDropMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87924D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.metal */; };
AB00F87C24D7F45E00413EE8 /* MetalVideoProcessUpDropMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87A24D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.swift */; };
AB00F87F24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87D24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.metal */; };
AB00F88024D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB00F87E24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.swift */; };
AB4C45D024D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45CE24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.swift */; };
AB4C45D124D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45CF24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.metal */; };
AB4C45D524D0125700315A52 /* MetalVideoProcessSwirlMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45D324D0125700315A52 /* MetalVideoProcessSwirlMotion.swift */; };
AB4C45D624D0125700315A52 /* MetalVideoProcessSwirlMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45D424D0125700315A52 /* MetalVideoProcessSwirlMotion.metal */; };
AB4C45D924D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45D724D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.swift */; };
AB4C45DA24D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45D824D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.metal */; };
AB4C45DD24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45DB24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.metal */; };
AB4C45DE24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45DC24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.swift */; };
AB4C45E124D0208C00315A52 /* MetalVideoProcessMoveRightMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45DF24D0208C00315A52 /* MetalVideoProcessMoveRightMotion.metal */; };
AB4C45E224D0208C00315A52 /* MetalVideoProcessMoveRightMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45E024D0208C00315A52 /* MetalVideoProcessMoveRightMotion.swift */; };
AB4C45E524D0255600315A52 /* MetalVideoProcessMoveDownMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45E324D0255600315A52 /* MetalVideoProcessMoveDownMotion.swift */; };
AB4C45E624D0255600315A52 /* MetalVideoProcessMoveDownMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4C45E424D0255600315A52 /* MetalVideoProcessMoveDownMotion.metal */; };
AB4F025F24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4F025D24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.swift */; };
AB4F026024D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4F025E24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.metal */; };
AB4F026324D7BC4600D0C673 /* MetalVideoProcessWiperMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB4F026124D7BC4600D0C673 /* MetalVideoProcessWiperMotion.metal */; };
AB4F026424D7BC4600D0C673 /* MetalVideoProcessWiperMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4F026224D7BC4600D0C673 /* MetalVideoProcessWiperMotion.swift */; };
AB59B92924C831D900943BE2 /* MetalVideoProcessCubeTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B91B24C831D700943BE2 /* MetalVideoProcessCubeTransition.metal */; };
AB59B92A24C831D900943BE2 /* MetalVideoProcessShanBaiTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B91C24C831D700943BE2 /* MetalVideoProcessShanBaiTransition.swift */; };
AB59B92B24C831D900943BE2 /* MetalVideoProcessFadeTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B91D24C831D700943BE2 /* MetalVideoProcessFadeTransition.swift */; };
AB59B92C24C831D900943BE2 /* MetalVideoProcessBurnTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B91E24C831D800943BE2 /* MetalVideoProcessBurnTransition.metal */; };
AB59B92D24C831D900943BE2 /* MetalVideoProcessFadeTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B91F24C831D800943BE2 /* MetalVideoProcessFadeTransition.metal */; };
AB59B92E24C831D900943BE2 /* MetalVideoProcessMirrorRotateTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92024C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.swift */; };
AB59B92F24C831D900943BE2 /* MetalVideoProcessMirrorRotateTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92124C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.metal */; };
AB59B93024C831D900943BE2 /* MetalVideoProcessBurnTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92224C831D800943BE2 /* MetalVideoProcessBurnTransition.swift */; };
AB59B93124C831D900943BE2 /* MetalVideoProcessEraseDownTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92324C831D800943BE2 /* MetalVideoProcessEraseDownTransition.metal */; };
AB59B93224C831D900943BE2 /* MetalVideoProcessShanBaiTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92424C831D800943BE2 /* MetalVideoProcessShanBaiTransition.metal */; };
AB59B93324C831D900943BE2 /* MetalVideoProcessReflectTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92524C831D800943BE2 /* MetalVideoProcessReflectTransition.swift */; };
AB59B93424C831D900943BE2 /* MetalVideoProcessReflectTransition.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92624C831D900943BE2 /* MetalVideoProcessReflectTransition.metal */; };
AB59B93524C831D900943BE2 /* MetalVideoProcessEraseDownTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92724C831D900943BE2 /* MetalVideoProcessEraseDownTransition.swift */; };
AB59B93624C831D900943BE2 /* MetalVideoProcessCubeTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B92824C831D900943BE2 /* MetalVideoProcessCubeTransition.swift */; };
AB59B94124C921F300943BE2 /* MetalVideoProcessRotateMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B93F24C921F300943BE2 /* MetalVideoProcessRotateMotion.swift */; };
AB59B94224C921F300943BE2 /* MetalVideoProcessRotateMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94024C921F300943BE2 /* MetalVideoProcessRotateMotion.metal */; };
AB59B94924C9672C00943BE2 /* MetalVideoProcessMoveUpMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94724C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.metal */; };
AB59B94A24C9672C00943BE2 /* MetalVideoProcessMoveUpMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94824C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.swift */; };
AB59B94D24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94B24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.metal */; };
AB59B94E24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB59B94C24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.swift */; };
AB603FA424D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB603FA224D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.metal */; };
AB603FA524D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB603FA324D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.swift */; };
AB603FE524D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB603FE324D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.swift */; };
AB603FE624D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB603FE424D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.metal */; };
AB7B931E24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB7B931C24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.metal */; };
AB7B931F24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B931D24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.swift */; };
AB7B932224D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.metal in Sources */ = {isa = PBXBuildFile; fileRef = AB7B932024D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.metal */; };
AB7B932324D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B932124D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
6A24C9FB24ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessEraseLeftTransition.metal; sourceTree = "<group>"; };
6A24C9FC24ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessVerticalUpGlitchTransition.metal; sourceTree = "<group>"; };
6A24C9FD24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessEraseRightTransition.metal; sourceTree = "<group>"; };
6A24C9FE24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessEraseRightTransition.swift; sourceTree = "<group>"; };
6A24C9FF24ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMorphTransition.swift; sourceTree = "<group>"; };
6A24CA0024ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessEraseUpTransition.metal; sourceTree = "<group>"; };
6A24CA0124ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessVerticalUpGlitchTransition.swift; sourceTree = "<group>"; };
6A24CA0224ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessEraseUpTransition.swift; sourceTree = "<group>"; };
6A24CA0324ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessEraseLeftTransition.swift; sourceTree = "<group>"; };
6A24CA0424ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMorphTransition.metal; sourceTree = "<group>"; };
6A24CA0524ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessCircleEraseTransition.swift; sourceTree = "<group>"; };
6A24CA0624ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessCircleEraseTransition.metal; sourceTree = "<group>"; };
6A3E705824BD9099009A0AC9 /* MetalVideoProcessTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessTransition.swift; sourceTree = "<group>"; };
6A3E708724C0448B009A0AC9 /* MetalVideoProcessFadeTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessFadeTransition.swift; sourceTree = "<group>"; };
6A3E708924C044A0009A0AC9 /* MetalVideoProcessFadeTransition.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessFadeTransition.metal; sourceTree = "<group>"; };
6A874BC124C594EC00ADFD8A /* MetalVideoProcessMotion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMotion.swift; sourceTree = "<group>"; };
6A874BCF24C6E1C200ADFD8A /* MetalVideoProcessFadeInMotion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessFadeInMotion.swift; sourceTree = "<group>"; };
6A874BD324C6F98100ADFD8A /* MetalVideoProcessFadeMotion.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessFadeMotion.metal; sourceTree = "<group>"; };
@@ -181,14 +245,58 @@
6ADB072824ADCC250010A817 /* MetalVideoProcessColorFilter.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessColorFilter.metal; sourceTree = "<group>"; };
6ADB07D524AED1260010A817 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
6ADB07E024AEDC9B0010A817 /* default.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = default.metal; sourceTree = "<group>"; };
AB17C20E24C17D770023C974 /* MetalVideoProcessEraseDownTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessEraseDownTransition.swift; sourceTree = "<group>"; };
AB17C20F24C17D770023C974 /* MetalVideoProcessEraseDownTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessEraseDownTransition.metal; sourceTree = "<group>"; };
AB17C21224C182780023C974 /* MetalVideoProcessMirrorRotateTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMirrorRotateTransition.metal; sourceTree = "<group>"; };
AB17C21324C182780023C974 /* MetalVideoProcessMirrorRotateTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMirrorRotateTransition.swift; sourceTree = "<group>"; };
AB17C21624C5737B0023C974 /* MetalVideoProcessReflectTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessReflectTransition.metal; sourceTree = "<group>"; };
AB17C21724C5737B0023C974 /* MetalVideoProcessReflectTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessReflectTransition.swift; sourceTree = "<group>"; };
AB64836024C03E6200A0266B /* MetalVideoProcessShanBaiTrans.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessShanBaiTrans.metal; sourceTree = "<group>"; };
AB64836124C03E6200A0266B /* MetalVideoProcessShanBaiTrans.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessShanBaiTrans.swift; sourceTree = "<group>"; };
AB00F87124D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessPendulumMotion.metal; sourceTree = "<group>"; };
AB00F87224D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessPendulumMotion.swift; sourceTree = "<group>"; };
AB00F87524D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessRightDropMotion.metal; sourceTree = "<group>"; };
AB00F87624D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessRightDropMotion.swift; sourceTree = "<group>"; };
AB00F87924D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessUpDropMotion.metal; sourceTree = "<group>"; };
AB00F87A24D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessUpDropMotion.swift; sourceTree = "<group>"; };
AB00F87D24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessZoomInBlurMotion.metal; sourceTree = "<group>"; };
AB00F87E24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessZoomInBlurMotion.swift; sourceTree = "<group>"; };
AB4C45CE24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessRotateInRightMotion.swift; sourceTree = "<group>"; };
AB4C45CF24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessRotateInRightMotion.metal; sourceTree = "<group>"; };
AB4C45D324D0125700315A52 /* MetalVideoProcessSwirlMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessSwirlMotion.swift; sourceTree = "<group>"; };
AB4C45D424D0125700315A52 /* MetalVideoProcessSwirlMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessSwirlMotion.metal; sourceTree = "<group>"; };
AB4C45D724D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessZoomOutMotion.swift; sourceTree = "<group>"; };
AB4C45D824D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessZoomOutMotion.metal; sourceTree = "<group>"; };
AB4C45DB24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMoveLeftMotion.metal; sourceTree = "<group>"; };
AB4C45DC24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMoveLeftMotion.swift; sourceTree = "<group>"; };
AB4C45DF24D0208C00315A52 /* MetalVideoProcessMoveRightMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMoveRightMotion.metal; sourceTree = "<group>"; };
AB4C45E024D0208C00315A52 /* MetalVideoProcessMoveRightMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMoveRightMotion.swift; sourceTree = "<group>"; };
AB4C45E324D0255600315A52 /* MetalVideoProcessMoveDownMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMoveDownMotion.swift; sourceTree = "<group>"; };
AB4C45E424D0255600315A52 /* MetalVideoProcessMoveDownMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMoveDownMotion.metal; sourceTree = "<group>"; };
AB4F025D24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessUpMoveInBlurIIMotion.swift; sourceTree = "<group>"; };
AB4F025E24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessUpMoveInBlurIIMotion.metal; sourceTree = "<group>"; };
AB4F026124D7BC4600D0C673 /* MetalVideoProcessWiperMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessWiperMotion.metal; sourceTree = "<group>"; };
AB4F026224D7BC4600D0C673 /* MetalVideoProcessWiperMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessWiperMotion.swift; sourceTree = "<group>"; };
AB59B91B24C831D700943BE2 /* MetalVideoProcessCubeTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessCubeTransition.metal; sourceTree = "<group>"; };
AB59B91C24C831D700943BE2 /* MetalVideoProcessShanBaiTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessShanBaiTransition.swift; sourceTree = "<group>"; };
AB59B91D24C831D700943BE2 /* MetalVideoProcessFadeTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessFadeTransition.swift; sourceTree = "<group>"; };
AB59B91E24C831D800943BE2 /* MetalVideoProcessBurnTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessBurnTransition.metal; sourceTree = "<group>"; };
AB59B91F24C831D800943BE2 /* MetalVideoProcessFadeTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessFadeTransition.metal; sourceTree = "<group>"; };
AB59B92024C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMirrorRotateTransition.swift; sourceTree = "<group>"; };
AB59B92124C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMirrorRotateTransition.metal; sourceTree = "<group>"; };
AB59B92224C831D800943BE2 /* MetalVideoProcessBurnTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessBurnTransition.swift; sourceTree = "<group>"; };
AB59B92324C831D800943BE2 /* MetalVideoProcessEraseDownTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessEraseDownTransition.metal; sourceTree = "<group>"; };
AB59B92424C831D800943BE2 /* MetalVideoProcessShanBaiTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessShanBaiTransition.metal; sourceTree = "<group>"; };
AB59B92524C831D800943BE2 /* MetalVideoProcessReflectTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessReflectTransition.swift; sourceTree = "<group>"; };
AB59B92624C831D900943BE2 /* MetalVideoProcessReflectTransition.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessReflectTransition.metal; sourceTree = "<group>"; };
AB59B92724C831D900943BE2 /* MetalVideoProcessEraseDownTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessEraseDownTransition.swift; sourceTree = "<group>"; };
AB59B92824C831D900943BE2 /* MetalVideoProcessCubeTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessCubeTransition.swift; sourceTree = "<group>"; };
AB59B93F24C921F300943BE2 /* MetalVideoProcessRotateMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessRotateMotion.swift; sourceTree = "<group>"; };
AB59B94024C921F300943BE2 /* MetalVideoProcessRotateMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessRotateMotion.metal; sourceTree = "<group>"; };
AB59B94724C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMoveUpMotion.metal; sourceTree = "<group>"; };
AB59B94824C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMoveUpMotion.swift; sourceTree = "<group>"; };
AB59B94B24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessZoomInMotion.metal; sourceTree = "<group>"; };
AB59B94C24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessZoomInMotion.swift; sourceTree = "<group>"; };
AB603FA224D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessSlimZoomInMotion.metal; sourceTree = "<group>"; };
AB603FA324D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessSlimZoomInMotion.swift; sourceTree = "<group>"; };
AB603FE324D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessMirrorRotateMotion.swift; sourceTree = "<group>"; };
AB603FE424D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessMirrorRotateMotion.metal; sourceTree = "<group>"; };
AB7B931C24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessUpMoveInBlurMotion.metal; sourceTree = "<group>"; };
AB7B931D24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessUpMoveInBlurMotion.swift; sourceTree = "<group>"; };
AB7B932024D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = MetalVideoProcessZoomOutBlurMotion.metal; sourceTree = "<group>"; };
AB7B932124D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalVideoProcessZoomOutBlurMotion.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -205,6 +313,44 @@
6A874BCE24C6E10500ADFD8A /* Motions */ = {
isa = PBXGroup;
children = (
AB7B932024D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.metal */,
AB7B932124D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.swift */,
AB00F87D24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.metal */,
AB00F87E24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.swift */,
AB00F87924D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.metal */,
AB00F87A24D7F45D00413EE8 /* MetalVideoProcessUpDropMotion.swift */,
AB00F87524D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.metal */,
AB00F87624D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.swift */,
AB00F87124D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.metal */,
AB00F87224D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.swift */,
AB4F026124D7BC4600D0C673 /* MetalVideoProcessWiperMotion.metal */,
AB4F026224D7BC4600D0C673 /* MetalVideoProcessWiperMotion.swift */,
AB4F025E24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.metal */,
AB4F025D24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.swift */,
AB603FE424D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.metal */,
AB603FE324D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.swift */,
AB603FA224D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.metal */,
AB603FA324D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.swift */,
AB7B931C24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.metal */,
AB7B931D24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.swift */,
AB4C45E424D0255600315A52 /* MetalVideoProcessMoveDownMotion.metal */,
AB4C45E324D0255600315A52 /* MetalVideoProcessMoveDownMotion.swift */,
AB4C45DF24D0208C00315A52 /* MetalVideoProcessMoveRightMotion.metal */,
AB4C45E024D0208C00315A52 /* MetalVideoProcessMoveRightMotion.swift */,
AB4C45DB24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.metal */,
AB4C45DC24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.swift */,
AB59B94724C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.metal */,
AB59B94824C9672B00943BE2 /* MetalVideoProcessMoveUpMotion.swift */,
AB4C45D824D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.metal */,
AB4C45D724D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.swift */,
AB4C45D424D0125700315A52 /* MetalVideoProcessSwirlMotion.metal */,
AB4C45D324D0125700315A52 /* MetalVideoProcessSwirlMotion.swift */,
AB4C45CF24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.metal */,
AB4C45CE24D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.swift */,
AB59B94B24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.metal */,
AB59B94C24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.swift */,
AB59B94024C921F300943BE2 /* MetalVideoProcessRotateMotion.metal */,
AB59B93F24C921F300943BE2 /* MetalVideoProcessRotateMotion.swift */,
6A874BCF24C6E1C200ADFD8A /* MetalVideoProcessFadeInMotion.swift */,
6A874BD324C6F98100ADFD8A /* MetalVideoProcessFadeMotion.metal */,
6A874BD624C6FD5100ADFD8A /* MetalVideoProcessFadeOutMotion.swift */,
@@ -461,16 +607,32 @@
AB64835724C03CEA00A0266B /* Transitions */ = {
isa = PBXGroup;
children = (
AB17C21624C5737B0023C974 /* MetalVideoProcessReflectTransition.metal */,
AB17C21724C5737B0023C974 /* MetalVideoProcessReflectTransition.swift */,
AB17C21224C182780023C974 /* MetalVideoProcessMirrorRotateTransition.metal */,
AB17C21324C182780023C974 /* MetalVideoProcessMirrorRotateTransition.swift */,
AB17C20F24C17D770023C974 /* MetalVideoProcessEraseDownTransition.metal */,
AB17C20E24C17D770023C974 /* MetalVideoProcessEraseDownTransition.swift */,
AB64836024C03E6200A0266B /* MetalVideoProcessShanBaiTrans.metal */,
AB64836124C03E6200A0266B /* MetalVideoProcessShanBaiTrans.swift */,
6A3E708724C0448B009A0AC9 /* MetalVideoProcessFadeTransition.swift */,
6A3E708924C044A0009A0AC9 /* MetalVideoProcessFadeTransition.metal */,
6A24CA0624ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.metal */,
6A24CA0524ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.swift */,
6A24C9FB24ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.metal */,
6A24CA0324ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.swift */,
6A24C9FD24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.metal */,
6A24C9FE24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.swift */,
6A24CA0024ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.metal */,
6A24CA0224ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.swift */,
6A24CA0424ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.metal */,
6A24C9FF24ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.swift */,
6A24C9FC24ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.metal */,
6A24CA0124ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.swift */,
AB59B91E24C831D800943BE2 /* MetalVideoProcessBurnTransition.metal */,
AB59B92224C831D800943BE2 /* MetalVideoProcessBurnTransition.swift */,
AB59B91B24C831D700943BE2 /* MetalVideoProcessCubeTransition.metal */,
AB59B92824C831D900943BE2 /* MetalVideoProcessCubeTransition.swift */,
AB59B92324C831D800943BE2 /* MetalVideoProcessEraseDownTransition.metal */,
AB59B92724C831D900943BE2 /* MetalVideoProcessEraseDownTransition.swift */,
AB59B91F24C831D800943BE2 /* MetalVideoProcessFadeTransition.metal */,
AB59B91D24C831D700943BE2 /* MetalVideoProcessFadeTransition.swift */,
AB59B92124C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.metal */,
AB59B92024C831D800943BE2 /* MetalVideoProcessMirrorRotateTransition.swift */,
AB59B92624C831D900943BE2 /* MetalVideoProcessReflectTransition.metal */,
AB59B92524C831D800943BE2 /* MetalVideoProcessReflectTransition.swift */,
AB59B92424C831D800943BE2 /* MetalVideoProcessShanBaiTransition.metal */,
AB59B91C24C831D700943BE2 /* MetalVideoProcessShanBaiTransition.swift */,
);
path = Transitions;
sourceTree = "<group>";
@@ -559,86 +721,140 @@
files = (
6ADB072A24ADCC250010A817 /* MetalVideoProcessColorFilter.swift in Sources */,
6ADB071124ADC4DC0010A817 /* MetalVideoProcessMovieWriter.swift in Sources */,
AB17C21824C5737B0023C974 /* MetalVideoProcessReflectTransition.metal in Sources */,
AB4C45E624D0255600315A52 /* MetalVideoProcessMoveDownMotion.metal in Sources */,
6A24CA1124ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.swift in Sources */,
6ADB06A224ADB3B70010A817 /* CompositionProvider.swift in Sources */,
6ADB06F924ADBE1D0010A817 /* YUVToRGBConversion.metal in Sources */,
6A3E708824C0448B009A0AC9 /* MetalVideoProcessFadeTransition.swift in Sources */,
AB17C21024C17D770023C974 /* MetalVideoProcessEraseDownTransition.swift in Sources */,
AB59B92B24C831D900943BE2 /* MetalVideoProcessFadeTransition.swift in Sources */,
AB00F87824D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.swift in Sources */,
6ADB065E24AD8B7E0010A817 /* MetalRendering.swift in Sources */,
6ADB06B524ADB3B70010A817 /* CMTimeExtensition.swift in Sources */,
AB59B94924C9672C00943BE2 /* MetalVideoProcessMoveUpMotion.metal in Sources */,
AB4C45D024D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.swift in Sources */,
6ADB06B024ADB3B70010A817 /* AVAssetReverseImageResource.swift in Sources */,
6ADB070524ADC4100010A817 /* PictureOutput.swift in Sources */,
AB4C45DE24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.swift in Sources */,
AB4C45D624D0125700315A52 /* MetalVideoProcessSwirlMotion.metal in Sources */,
6A874BD424C6F98100ADFD8A /* MetalVideoProcessFadeMotion.metal in Sources */,
6ADB070F24ADC4DC0010A817 /* MetalVideoProcessPlayer.swift in Sources */,
6ADB06AE24ADB3B70010A817 /* Resource.swift in Sources */,
6ADB06B624ADB3B70010A817 /* ImageGeneratorExtension.swift in Sources */,
6A874BD724C6FD5100ADFD8A /* MetalVideoProcessFadeOutMotion.swift in Sources */,
6A874BD024C6E1C200ADFD8A /* MetalVideoProcessFadeInMotion.swift in Sources */,
AB64836324C03E6200A0266B /* MetalVideoProcessShanBaiTrans.swift in Sources */,
6ADB069B24ADB3B70010A817 /* VideoCompositor.swift in Sources */,
6ADB066C24AD8B7E0010A817 /* ImageOrientation.swift in Sources */,
AB59B93524C831D900943BE2 /* MetalVideoProcessEraseDownTransition.swift in Sources */,
6ADB069E24ADB3B70010A817 /* AudioMixer.swift in Sources */,
6A24CA1224ECCC4600F9D8BC /* MetalVideoProcessCircleEraseTransition.metal in Sources */,
AB59B94E24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.swift in Sources */,
6ADB071824ADC4F80010A817 /* MetalVideoProcessTransformFilter.metal in Sources */,
AB59B93124C831D900943BE2 /* MetalVideoProcessEraseDownTransition.metal in Sources */,
AB00F87724D7EFBC00413EE8 /* MetalVideoProcessRightDropMotion.metal in Sources */,
AB59B93424C831D900943BE2 /* MetalVideoProcessReflectTransition.metal in Sources */,
AB59B92A24C831D900943BE2 /* MetalVideoProcessShanBaiTransition.swift in Sources */,
6ADB065824AD8B7E0010A817 /* Color.swift in Sources */,
AB00F87F24D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.metal in Sources */,
6ADB06A324ADB3B70010A817 /* AudioTransition.swift in Sources */,
AB00F87324D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.metal in Sources */,
AB59B93624C831D900943BE2 /* MetalVideoProcessCubeTransition.swift in Sources */,
AB7B931F24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.swift in Sources */,
AB00F87C24D7F45E00413EE8 /* MetalVideoProcessUpDropMotion.swift in Sources */,
6ADB069C24ADB3B70010A817 /* CompositionGenerator.swift in Sources */,
6ADB06EF24ADBBDA0010A817 /* SolidColorGenerator.swift in Sources */,
6A24CA0824ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.metal in Sources */,
AB59B94124C921F300943BE2 /* MetalVideoProcessRotateMotion.swift in Sources */,
6ADB06AD24ADB3B70010A817 /* AVAssetTrackResource.swift in Sources */,
AB00F87424D7ED4300413EE8 /* MetalVideoProcessPendulumMotion.swift in Sources */,
6ADB066B24AD8B7E0010A817 /* MetalRenderingDevice.swift in Sources */,
AB17C21524C182780023C974 /* MetalVideoProcessMirrorRotateTransition.swift in Sources */,
6ADB06A124ADB3B70010A817 /* Timeline.swift in Sources */,
6ADB06B324ADB3B70010A817 /* Log.swift in Sources */,
AB17C21424C182780023C974 /* MetalVideoProcessMirrorRotateTransition.metal in Sources */,
AB4F026324D7BC4600D0C673 /* MetalVideoProcessWiperMotion.metal in Sources */,
6ADB065D24AD8B7E0010A817 /* Size.swift in Sources */,
AB4C45D124D00ED100315A52 /* MetalVideoProcessRotateInRightMotion.metal in Sources */,
6ADB070024ADC1930010A817 /* MetalVideoProcessBlendFilter.metal in Sources */,
6A874BC224C594EC00ADFD8A /* MetalVideoProcessMotion.swift in Sources */,
AB7B932224D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.metal in Sources */,
6ADB06B124ADB3B70010A817 /* CoreImageExtension.swift in Sources */,
AB603FE624D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.metal in Sources */,
6ADB06E024ADB8B80010A817 /* MetalVideoProcessOperation.swift in Sources */,
6ADB071A24ADC4F80010A817 /* MetalVideoProcessBeautyFilter.metal in Sources */,
6ADB06EB24ADBBDA0010A817 /* ImageGenerator.swift in Sources */,
6ADB06B724ADB3B70010A817 /* TimeRangeExtension.swift in Sources */,
AB4C45D524D0125700315A52 /* MetalVideoProcessSwirlMotion.swift in Sources */,
6ADB06CA24ADB55D0010A817 /* MetalVideoEditor.swift in Sources */,
6ADB07E124AEDC9B0010A817 /* default.metal in Sources */,
6ADB06ED24ADBBDA0010A817 /* PictureInput.swift in Sources */,
AB64836224C03E6200A0266B /* MetalVideoProcessShanBaiTrans.metal in Sources */,
6ADB06A624ADB3B70010A817 /* TrackConfiguration.swift in Sources */,
6ADB071224ADC4DC0010A817 /* MetalVideoProcessCompositionRequestCache.swift in Sources */,
6ADB06A824ADB3B70010A817 /* ImageCompositionGroupProvider.swift in Sources */,
6ADB06FF24ADC1930010A817 /* MetalVideoProcessBlendFilter.swift in Sources */,
6ADB066124AD8B7E0010A817 /* Timestamp.swift in Sources */,
6ADB071724ADC4F80010A817 /* MetalVideoProcessBeautyFilter.swift in Sources */,
AB59B93324C831D900943BE2 /* MetalVideoProcessReflectTransition.swift in Sources */,
6ADB06AF24ADB3B70010A817 /* AVAssetReaderImageResource.swift in Sources */,
6A24CA1024ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.metal in Sources */,
6ADB072B24ADCC250010A817 /* MetalVideoProcessColorFilter.metal in Sources */,
6A24CA0C24ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.metal in Sources */,
AB4C45D924D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.swift in Sources */,
6ADB072624ADCC1C0010A817 /* MetalVideoProcessBackground.swift in Sources */,
AB4C45E524D0255600315A52 /* MetalVideoProcessMoveDownMotion.swift in Sources */,
AB59B93024C831D900943BE2 /* MetalVideoProcessBurnTransition.swift in Sources */,
AB7B931E24D15470006D0761 /* MetalVideoProcessUpMoveInBlurMotion.metal in Sources */,
AB603FE524D7AA4A008BF603 /* MetalVideoProcessMirrorRotateMotion.swift in Sources */,
AB4F026424D7BC4600D0C673 /* MetalVideoProcessWiperMotion.swift in Sources */,
AB603FA524D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.swift in Sources */,
6ADB071D24ADC8750010A817 /* MetalVideoProcessCompositor.swift in Sources */,
6ADB06B424ADB3B70010A817 /* CGMathFunctions.swift in Sources */,
6AA561D324BC57F3004894F7 /* MetalVideoProcessLuminance.metal in Sources */,
6ADB065A24AD8B7E0010A817 /* Matrix.swift in Sources */,
AB603FA424D2AD77008BF603 /* MetalVideoProcessSlimZoomInMotion.metal in Sources */,
6ADB069F24ADB3B70010A817 /* AudioProcessingChain.swift in Sources */,
AB59B92E24C831D900943BE2 /* MetalVideoProcessMirrorRotateTransition.swift in Sources */,
AB59B92F24C831D900943BE2 /* MetalVideoProcessMirrorRotateTransition.metal in Sources */,
AB4F025F24D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.swift in Sources */,
6A24CA0E24ECCC4600F9D8BC /* MetalVideoProcessEraseUpTransition.swift in Sources */,
AB7B932324D1640E006D0761 /* MetalVideoProcessZoomOutBlurMotion.swift in Sources */,
AB59B94A24C9672C00943BE2 /* MetalVideoProcessMoveUpMotion.swift in Sources */,
6A24CA0724ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.metal in Sources */,
6ADB06A424ADB3B70010A817 /* ImageOverlayItem.swift in Sources */,
AB17C21924C5737B0023C974 /* MetalVideoProcessReflectTransition.swift in Sources */,
AB17C21124C17D770023C974 /* MetalVideoProcessEraseDownTransition.metal in Sources */,
AB4C45DD24D01E3600315A52 /* MetalVideoProcessMoveLeftMotion.metal in Sources */,
AB59B92C24C831D900943BE2 /* MetalVideoProcessBurnTransition.metal in Sources */,
AB4C45DA24D01B7D00315A52 /* MetalVideoProcessZoomOutMotion.metal in Sources */,
AB4C45E224D0208C00315A52 /* MetalVideoProcessMoveRightMotion.swift in Sources */,
AB00F88024D7F5E900413EE8 /* MetalVideoProcessZoomInBlurMotion.swift in Sources */,
6ADB071924ADC4F80010A817 /* MetalVideoProcessTransformFilter.swift in Sources */,
6A3E708A24C044A0009A0AC9 /* MetalVideoProcessFadeTransition.metal in Sources */,
6ADB06FC24ADBF8F0010A817 /* MetalVideoProcessRenderView.swift in Sources */,
6ADB06AB24ADB3B70010A817 /* ImageResource.swift in Sources */,
6AA561D424BC57F3004894F7 /* MetalVideoProcessLuminance.swift in Sources */,
6ADB06A724ADB3B70010A817 /* TrackItem.swift in Sources */,
AB59B94D24C96E3100943BE2 /* MetalVideoProcessZoomInMotion.metal in Sources */,
6ADB066924AD8B7E0010A817 /* ShaderUniformSettings.swift in Sources */,
AB59B94224C921F300943BE2 /* MetalVideoProcessRotateMotion.metal in Sources */,
6ADB071024ADC4DC0010A817 /* MetalVideoProcessPlayer+CompositorDelegate.swift in Sources */,
6A24CA0B24ECCC4600F9D8BC /* MetalVideoProcessMorphTransition.swift in Sources */,
6ADB06B224ADB3B70010A817 /* TimingFunctionFactory.swift in Sources */,
AB4F026024D7B20300D0C673 /* MetalVideoProcessUpMoveInBlurIIMotion.metal in Sources */,
6ADB072424ADCBC30010A817 /* MetalVideoProcessGaussianBlurFilter.swift in Sources */,
AB59B92924C831D900943BE2 /* MetalVideoProcessCubeTransition.metal in Sources */,
6ADB070624ADC4100010A817 /* RenderView.swift in Sources */,
6A24CA0924ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.metal in Sources */,
6A24CA0F24ECCC4600F9D8BC /* MetalVideoProcessEraseLeftTransition.swift in Sources */,
6A24CA0A24ECCC4600F9D8BC /* MetalVideoProcessEraseRightTransition.swift in Sources */,
6A24CA0D24ECCC4600F9D8BC /* MetalVideoProcessVerticalUpGlitchTransition.swift in Sources */,
6ADB06F824ADBE1D0010A817 /* YUVToRGBConversion.swift in Sources */,
6ADB069D24ADB3B70010A817 /* AudioProcessingTapHolder.swift in Sources */,
6ADB067024AD8B7E0010A817 /* Position.swift in Sources */,
6ADB06A024ADB3B70010A817 /* VideoTransition.swift in Sources */,
6ADB067124AD8B7E0010A817 /* Texture.swift in Sources */,
6ADB069A24ADB3B70010A817 /* VideoCompositionInstruction.swift in Sources */,
AB00F87B24D7F45E00413EE8 /* MetalVideoProcessUpDropMotion.metal in Sources */,
6ADB072224ADCB2A0010A817 /* MetalVideoProcessFilterGroup.swift in Sources */,
6ADB06AC24ADB3B70010A817 /* PHAssetImageResource.swift in Sources */,
AB59B92D24C831D900943BE2 /* MetalVideoProcessFadeTransition.metal in Sources */,
6ADB066024AD8B7E0010A817 /* Pipeline.swift in Sources */,
6ADB06A924ADB3B70010A817 /* PHAssetLivePhotoResource.swift in Sources */,
AB4C45E124D0208C00315A52 /* MetalVideoProcessMoveRightMotion.metal in Sources */,
AB59B93224C831D900943BE2 /* MetalVideoProcessShanBaiTransition.metal in Sources */,
6A3E705924BD9099009A0AC9 /* MetalVideoProcessTransition.swift in Sources */,
6ADB06FA24ADBE1D0010A817 /* Camera.swift in Sources */,
6ADB06AA24ADB3B70010A817 /* PHAssetTrackResource.swift in Sources */,
@@ -774,7 +990,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -801,7 +1017,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = RYRPKMVKDL;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -210,8 +210,9 @@ public class ShaderUniformSettings {
shaderUniformSettingsQueue.sync {
guard (uniformValues.count > 0) else { return }
let cont: Int = ((uniformValues.count + 3) / 4) * 4//Int(ceil(Double(uniformValues.count) / 4.0)) * 4
let uniformBuffer = self.currentDevice.device.makeBuffer(bytes: uniformValues,
length: uniformValues.count * MemoryLayout<Float>.size,
length: cont * MemoryLayout<Float>.size,
options: [])!
let iGlobalTimeBuffer = self.currentDevice.device.makeBuffer(bytes: &self.iGlobalTime, length: MemoryLayout<Float>.size, options: MTLResourceOptions.storageModeShared)!
@@ -1,6 +1,7 @@
import Foundation
import AVFoundation
import Metal
import CoreImage
public protocol CameraDelegate {
func didCaptureBuffer(_ sampleBuffer: CMSampleBuffer)
@@ -72,7 +73,10 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
var supportsFullYUVRange: Bool = false
let captureAsYUV: Bool
let yuvConversionRenderPipelineState: MTLRenderPipelineState?
// let yuvConversionRenderPipelineState: MTLRenderPipelineState?
/// Compute
var yuvConversionComputePipelineState: MTLComputePipelineState?
var yuvLookupTable: [String: (Int, MTLDataType)] = [: ]
let frameRenderingSemaphore = DispatchSemaphore(value: 1)
@@ -110,7 +114,7 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
self.videoInput = nil
self.videoOutput = nil
self.inputCamera = nil
self.yuvConversionRenderPipelineState = nil
self.yuvConversionComputePipelineState = nil
super.init()
throw CameraError()
}
@@ -121,7 +125,7 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
} catch {
self.videoInput = nil
self.videoOutput = nil
self.yuvConversionRenderPipelineState = nil
self.yuvConversionComputePipelineState = nil
super.init()
throw error
}
@@ -133,7 +137,12 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
// Add the video frame output
videoOutput = AVCaptureVideoDataOutput()
videoOutput.alwaysDiscardsLateVideoFrames = false
#if targetEnvironment(simulator)
self.yuvConversionRenderPipelineState = nil
super.init()
#else
if captureAsYUV {
supportsFullYUVRange = false
let supportedPixelFormats = videoOutput.availableVideoPixelFormatTypes
@@ -143,20 +152,20 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
}
}
if (supportsFullYUVRange) {
let (pipelineState, lookupTable) = generateRenderPipelineState(device: sharedMetalRenderingDevice, vertexFunctionName: "twoInputVertex", fragmentFunctionName: "yuvConversionFullRangeFragment", operationName: "YUVToRGB")
self.yuvConversionRenderPipelineState = pipelineState
let (pipelineState, lookupTable) = generateComputePipelineState(device: sharedMetalRenderingDevice, kernelFunctionName: "yuv2rgb", operationName: "YUVToRGB")
self.yuvLookupTable = lookupTable
self.yuvConversionComputePipelineState = pipelineState
videoOutput.videoSettings = [kCVPixelBufferMetalCompatibilityKey as String: true,
kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: Int32(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange))]
} else {
let (pipelineState, lookupTable) = generateRenderPipelineState(device: sharedMetalRenderingDevice, vertexFunctionName: "twoInputVertex", fragmentFunctionName: "yuvConversionVideoRangeFragment", operationName: "YUVToRGB")
self.yuvConversionRenderPipelineState = pipelineState
self.yuvLookupTable = lookupTable
videoOutput.videoSettings = [kCVPixelBufferMetalCompatibilityKey as String: true,
kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: Int32(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange))]
// let (pipelineState, lookupTable) = generateRenderPipelineState(device: sharedMetalRenderingDevice, vertexFunctionName: "twoInputVertex", fragmentFunctionName: "yuvConversionVideoRangeFragment", operationName: "YUVToRGB")
// self.yuvConversionRenderPipelineState = pipelineState
// self.yuvLookupTable = lookupTable
// videoOutput.videoSettings = [kCVPixelBufferMetalCompatibilityKey as String: true,
// kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: Int32(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange))]
}
} else {
self.yuvConversionRenderPipelineState = nil
self.yuvConversionComputePipelineState = nil
videoOutput.videoSettings = [kCVPixelBufferMetalCompatibilityKey as String: true,
kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: Int32(kCVPixelFormatType_32BGRA))]
}
@@ -173,6 +182,7 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
let _ = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, sharedMetalRenderingDevice.device, nil, &videoTextureCache)
videoOutput.setSampleBufferDelegate(self, queue: cameraProcessingQueue)
#endif
}
deinit {
@@ -228,11 +238,16 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer
outputHeight = bufferHeight
}
let outputTexture = Texture(device: sharedMetalRenderingDevice.device, orientation: .portrait, width: outputWidth, height: outputHeight, timingStyle: .videoFrame(timestamp: Timestamp(currentTime)))
convertYUVToRGB(pipelineState: self.yuvConversionRenderPipelineState!, lookupTable: self.yuvLookupTable,
luminanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: luminanceTexture),
chrominanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: chrominanceTexture),
resultTexture: outputTexture, colorConversionMatrix: conversionMatrix)
convertYUVtoRGBCompute(pipelineState: self.yuvConversionComputePipelineState!,
lookupTable: self.yuvLookupTable,
luminanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: luminanceTexture),
chrominanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: chrominanceTexture),
resultTexture: outputTexture,
colorConversionMatrix: conversionMatrix)
// convertYUVToRGB(pipelineState: self.yuvConversionRenderPipelineState!, lookupTable: self.yuvLookupTable,
// luminanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: luminanceTexture),
// chrominanceTexture: Texture(orientation: self.orientation ?? self.location.imageOrientation(), texture: chrominanceTexture),
// resultTexture: outputTexture, colorConversionMatrix: conversionMatrix)
texture = outputTexture
} else {
texture = nil
@@ -36,3 +36,22 @@ fragment half4 yuvConversionVideoRangeFragment(TwoInputVertexIO fragmentInput [[
return half4(rgb, 1.0);
}
constant float3 ColorOffsetFullRange = float3(0.0, -0.5, -0.5);
kernel void yuv2rgb(texture2d<float, access::read> y_tex [[ texture(0) ]],
texture2d<float, access::read> uv_tex [[ texture(1) ]],
texture2d<float, access::write> bgr_tex [[ texture(2) ]],
uint2 gid [[thread_position_in_grid]])
{
float3 yuv = float3(y_tex.read(gid).r, uv_tex.read(gid/2).rg) + ColorOffsetFullRange;
const float3x3 kColorConversion601Default = {
{1.164, 1.164, 1.164},
{0.0, -0.392, 2.017},
{1.596, -0.813, 0.0},
};
bgr_tex.write(float4(float3(kColorConversion601Default * yuv), 1.0), gid);
}
@@ -21,6 +21,30 @@ public let colorConversionMatrix709Default = Matrix3x3(rowMajorValues: [
1.793, -0.533, 0.0,
])
public func convertYUVtoRGBCompute(pipelineState: MTLComputePipelineState,
lookupTable: [String:(Int, MTLDataType)],
luminanceTexture: Texture,
chrominanceTexture: Texture,
secondChrominanceTexture: Texture? = nil,
resultTexture: Texture,
colorConversionMatrix: Matrix3x3,
device: MetalRenderingDevice = sharedMetalRenderingDevice) {
guard let commandBuffer = device.commandQueue.makeCommandBuffer() else {return}
let inputTextures: [UInt: Texture]
if let secondChrominanceTexture = secondChrominanceTexture {
inputTextures = [0: luminanceTexture, 1: chrominanceTexture, 2: secondChrominanceTexture]
} else {
inputTextures = [0: luminanceTexture, 1: chrominanceTexture]
}
let inputWidth = resultTexture.texture.width
let inputHeight = resultTexture.texture.height
commandBuffer.computeQuad(pipelineState: pipelineState, uniformSettings: nil, inputTextures: inputTextures, outputTexture: resultTexture, threadGroups: MTLSizeMake(inputWidth / 16, inputHeight / 16, 1))
commandBuffer.commit()
}
public func convertYUVToRGB(pipelineState: MTLRenderPipelineState, lookupTable: [String: (Int, MTLDataType)], luminanceTexture: Texture, chrominanceTexture: Texture, secondChrominanceTexture: Texture? = nil, resultTexture: Texture, colorConversionMatrix: Matrix3x3) {
let uniformSettings = ShaderUniformSettings(uniformLookupTable: lookupTable)
uniformSettings["colorConversionMatrix"] = colorConversionMatrix
@@ -10,11 +10,19 @@ import AVFoundation
public class MetalVideoProcessMotion: MetalVideoProcessOperation {
public var factor: Float = 1.0 { didSet { uniformSettings["factor"] = factor } }
public var factor: Float = 1.0 {
didSet { uniformSettings["factor"] = factor } }
public var roi: CGRect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) {
didSet {
uniformSettings["roi"] = Color(red: Float(roi.origin.x), green: Float(roi.origin.y), blue: Float(roi.width), alpha: Float(roi.height))
}
}
public var timingType: TimingFunctionType = .linearInterpolation
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if self.timelineRange.containsTime(texture.timingStyle.timestamp?.asCMTime ?? CMTime.invalid) {
let distance = CMTime(seconds: texture.frameTime, preferredTimescale: 1000) - self.timelineRange.start
let progress = Float(distance.seconds / self.timelineRange.duration.seconds)
@@ -143,7 +143,7 @@ public class MetalVideoProcessBackground: MetalVideoProcessFilterGroup {
}
}
public override init(trackID: Int32) {
public init(trackID: Int32) {
super.init(trackID: trackID)
self.trackID = trackID
@@ -6,11 +6,15 @@
//
import Foundation
import CoreMedia
import Metal
open class MetalVideoProcessFilterGroup: ImageProcessingOperation {
public var debugName: String = ""
public var trackID: Int32 = 0
/// FIlterZero
public var timelineRange: CMTimeRange = CMTimeRange.zero
public var trackID: Int32 = 0 //
public var isEnable: Bool = true
@@ -21,12 +25,36 @@ open class MetalVideoProcessFilterGroup: ImageProcessingOperation {
public var targets: TargetContainer { get { return outputImageRelay.targets } }
public let maximumInputs: UInt = 1
public init(trackID: Int32) {
public func saveUniformSettings(forTimelineRange timeline: CMTimeRange, trackID: Int32) {
self.timelineRange = timeline
self.trackID = trackID
}
public init(trackID: Int32, timelineRange: CMTimeRange = CMTimeRange.zero) {
self.timelineRange = timelineRange
self.trackID = trackID
}
public func checkTimelineRange(with texture: Texture) -> (Bool) {
if self.timelineRange == .zero {
return true
} else {
guard let time = texture.timingStyle.timestamp else {
let seconds = texture.timingStyle.timestamp!.asCMTime.seconds
let cmTime = CMTime(seconds: seconds, preferredTimescale: 1)
return self.timelineRange.containsTime(cmTime) ||
self.timelineRange.end == cmTime
}
return self.timelineRange.containsTime(time.asCMTime) || self.timelineRange.end == time.asCMTime
}
}
open func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
inputImageRelay.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
inputImageRelay.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
public func configureGroup(_ configurationOperation: (_ input: ImageRelay, _ output: ImageRelay) -> ()) {
@@ -17,8 +17,10 @@ public class MetalVideoProcessTransformFilter: MetalVideoProcessOperation {
public var roi: CGRect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) {
didSet {
self.translation = Position(Float(roi.origin.x * 2.0 + roi.size.width) - 1.0, Float(roi.origin.y * 2.0 + roi.size.height) - 1.0)
self.scale = Position(Float(roi.size.width), Float(roi.size.height))
let roiAdjusted = CGRect(x: roi.origin.x, y: 1.0 - roi.origin.y, width: roi.width, height: roi.height)
self.translation = Position(Float(roiAdjusted.origin.x * 2.0 + roiAdjusted.size.width) - 1.0, Float(roiAdjusted.origin.y * 2.0 - roiAdjusted.size.height) - 1.0)
self.scale = Position(Float(roiAdjusted.size.width), Float(roiAdjusted.size.height))
}
}
@@ -35,30 +35,35 @@ public class MetalVideoProcessPlayer: ImageSource {
public var trackID: Int32 = 0
deinit {
debugPrint("MetalMoviePlayer deinit: ", self.trackTargets)
debugPrint("##################MetalMoviePlayer deinit: ", self.trackTargets)
self.trackTargets.removeAll()
self.requestCache.removeAll()
}
public func dispose() {
self.audioEncodingTarget?.renderVideoFramesemaphore.signal()
self.audioEncodingTarget?.writeVideoFramesemaphore.signal()
self.exportVideoFramesemaphore.signal()
self.renderVideoFramesemaphore.signal()
self.suspend()
videoFrameProcessingQueue.sync { [weak self] in
guard let `self` = self else { return }
self.audioEncodingTarget?.renderVideoFramesemaphore.signal()
self.audioEncodingTarget?.writeVideoFramesemaphore.signal()
self.exportVideoFramesemaphore.signal()
self.renderVideoFramesemaphore.signal()
self.trackTargets.removeAll()
self.requestCache.removeAll()
self.player.pause()
self.exportCompositor?.delegate = nil
self.playbackCompositor?.delegate = nil
self.playbackDisplayLink?.isPaused = true
self.playbackDisplayLink?.invalidate()
self.playbackDisplayLink = nil
self.playerDelegate = nil
self.audioEncodingTarget = nil
currentRequest = nil
self.removeAllTargets()
NotificationCenter.default.removeObserver(self)
}
self.trackTargets.removeAll()
self.requestCache.removeAll()
self.player.pause()
self.exportCompositor?.delegate = nil
self.playbackCompositor?.delegate = nil
self.playbackDisplayLink?.isPaused = true
self.playbackDisplayLink?.invalidate()
self.playbackDisplayLink = nil
self.playerDelegate = nil
self.audioEncodingTarget = nil
currentRequest = nil
self.removeAllTargets()
NotificationCenter.default.removeObserver(self)
}
@@ -140,7 +145,7 @@ public class MetalVideoProcessPlayer: ImageSource {
self.playerItem = playerItem
self.player = AVPlayer(playerItem: self.playerItem)
self.player.volume = 1.0
self.playerItem.add(self.videoOutput)
self.videoOutput.requestNotificationOfMediaDataChange(withAdvanceInterval: 0.1)
@@ -155,6 +160,7 @@ public class MetalVideoProcessPlayer: ImageSource {
self.playbackCompositor = compositor
compositor.delegate = self
self.playerItem.preferredForwardBufferDuration = 0.5
self.seekTo(time: 0.0)
}
public func updatePlayerItem(playerItem: AVPlayerItem) {
@@ -519,13 +525,13 @@ public class MetalVideoProcessPlayer: ImageSource {
self.play()
}
}
}
public func suspend() {
self.playbackDisplayLink?.isPaused = true
self.videoFrameProcessingQueue.sync {
debugPrint("suspend ###################")
}
}
public func resume() {
@@ -561,50 +567,49 @@ public class MetalVideoProcessPlayer: ImageSource {
outputItemTime = self.videoOutput.itemTime(forHostTime: nextVSync)
if self.videoOutput.hasNewPixelBuffer(forItemTime: outputItemTime) {
autoreleasepool { [weak self] in
guard let `self` = self else { return }
guard let _ = self.videoOutput.copyPixelBuffer(forItemTime: outputItemTime,
itemTimeForDisplay: &newTime)
else {
return
guard let _ = self.videoOutput.copyPixelBuffer(forItemTime: outputItemTime,
itemTimeForDisplay: &newTime)
else {
return
}
self.requestOutputTime = newTime
if self.isPlaying {
debugPrint("prepare rendering: ", newTime.seconds)
guard let request = self.requestCache.getRequest(time: newTime) else {
self.requestCache.clearCacheWithTime(newTime)
return
}
self.requestOutputTime = newTime
if self.isPlaying {
debugPrint("prepare rendering: ", newTime.seconds)
guard let request = self.requestCache.getRequest(time: newTime) else {
self.requestCache.clearCacheWithTime(newTime)
self.currentRequest = request
autoreleasepool {
self.process(request: request, newTime: newTime)
}
self.requestCache.removeRequest(time: newTime)
} else {
autoreleasepool {
guard let request = self.currentRequest else {
return
}
self.currentRequest = request
autoreleasepool {
self.process(request: request, newTime: newTime)
}
self.requestCache.removeRequest(time: newTime)
} else {
autoreleasepool {
guard let request = self.currentRequest else {
return
}
self.process(request: request, newTime: request.compositionTime)
}
self.process(request: request, newTime: request.compositionTime)
}
if newTime != self.currentFrame {
self.currentFrame = newTime
if self.playerDelegate != nil {
self.playerDelegate?.playbackFrameTimeChanged(frameTime: self.currentFrame, player: self.player)
}
}
if sender.isPaused {
self.pause()
return
}
if newTime != self.currentFrame {
self.currentFrame = newTime
if self.playerDelegate != nil {
self.playerDelegate?.playbackFrameTimeChanged(frameTime: self.currentFrame, player: self.player)
}
//seek
}
} else {
autoreleasepool { [weak self] in
guard let `self` = self else { return }
guard let request = self.currentRequest else {
return
}
guard let request = self.currentRequest else {
return
}
autoreleasepool {
self.process(request: request, newTime: request.compositionTime)
}
if !self.isPlaying {
@@ -622,7 +627,9 @@ public class MetalVideoProcessPlayer: ImageSource {
}
func process(request: AVAsynchronousVideoCompositionRequest, newTime: CMTime) {
if self.playbackDisplayLink?.isPaused ?? true {
return
}
guard let instructions = request.videoCompositionInstruction as? VideoCompositionInstruction else {
return
}
@@ -20,13 +20,13 @@ public class MetalVideoProcessFadeInMotion: MetalVideoProcessMotion {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
}
debugPrint("fadein:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
@@ -13,6 +13,7 @@ using namespace metal;
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 fadeInMotion(TwoInputVertexIO fragmentInput [[stage_in]],
@@ -14,18 +14,17 @@ public class MetalVideoProcessFadeOutMotion: MetalVideoProcessMotion {
super.init(fragmentFunctionName: "fadeOutMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseOut
}
//
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time > timelineRange.end {
factor = 1.0
} else if time < timelineRange.start {
self.updateTargetsWithTexture(texture, trackID: trackID)
return
}
debugPrint("fadeout:", factor, " frameTime:", texture.frameTime)
// if let time = texture.timingStyle.timestamp?.asCMTime {
// if time > timelineRange.end {
// factor = 1.0
// }
//
//
// debugPrint("fadeout:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
// }
}
/// after fade out, we need the texture alpha keep to zero
@@ -0,0 +1,46 @@
//
// mirrorRotateMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace mirrorRotateMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 mirrorRotateMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g + uniform.roi.a * 0.5);
float2 uv = fragmentInput.textureCoordinate2;
float2 tempUv = uv;
float curve = uniform.factor;
half4 outCol = half4(0.0);
if (curve < 0.5) {
tempUv = float2((tempUv.x - center.x) * -(1. + curve * 200.0) + center.x, uv.y);
outCol = inputTexture2.sample(quadSampler, tempUv.xy);
} else {
tempUv = float2((tempUv.x - center.x) * (1. + (1.0 - curve) * 200.0) + center.x, uv.y);
outCol = inputTexture2.sample(quadSampler, tempUv.xy);
}
return half4(bgCol.rgb * (1. - outCol.a) + outCol.rgb, outCol.a);
}
}
@@ -0,0 +1,37 @@
//
// MetalVideoProcessMirrorRotateMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessMirrorRotateMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "mirrorRotateMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("mirrorRotateMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,38 @@
//
// moveDownMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace moveDownMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 moveDownMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
half4 fgCol = inputTexture2.sample(quadSampler, uv + float2(0.0, uniform.roi.a) - float2(0.0, uniform.roi.a * uniform.factor));
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,36 @@
//
// MetalVideoProcessMoveDownMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessMoveDownMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "moveDownMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseIn
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("moveUpMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,38 @@
//
// rotateMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace moveLeftMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 moveLeftMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
half4 fgCol = inputTexture2.sample(quadSampler, uv - float2(uniform.roi.b, 0.0) + float2(uniform.roi.b * uniform.factor, 0.0));
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,36 @@
//
// MetalVideoProcessMoveLeftMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessMoveLeftMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "moveLeftMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("moveLeftMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,38 @@
//
// moveRightMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace moveRightMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 moveRightMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
half4 fgCol = inputTexture2.sample(quadSampler, uv + float2(uniform.roi.b, 0.0) - float2(uniform.roi.b * uniform.factor, 0.0));
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,36 @@
//
// MetalVideoProcessMoveRightMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessMoveRightMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "moveRightMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("moveLeftMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,38 @@
//
// rotateMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace moveUpMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 moveUpMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
half4 fgCol = inputTexture2.sample(quadSampler, uv - float2(0.0, uniform.roi.a) + float2(0.0, uniform.roi.a * uniform.factor));
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,36 @@
//
// MetalVideoProcessMoveUpMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessMoveUpMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "moveUpMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseIn
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("moveUpMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,53 @@
//
// pendulumMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
#define PI 3.1415926
namespace pendulumMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 pendulumMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g);
float time = sin(uniform.factor * PI * 6.0) * exp(-uniform.factor);
float2 uv = fragmentInput.textureCoordinate2;
float rotCorner = time * 1.5707963;
float2 rot = float2(cos(rotCorner), sin(rotCorner));
uv = (uv - center) * uniform.iResolution;
uv = float2(rot.x * uv.x + rot.y * uv.y, -rot.y * uv.x + rot.x * uv.y);
uv = uv / uniform.iResolution + center;
float2 dir = float2(uv - center) * 0.5 * time;
dir = -float2(rot.x * dir.x + rot.y * dir.y, -rot.y * dir.x + rot.x * dir.y) * 0.6;
half4 fgCol = half4(0.0);
for(float i = 0.0; i < 1.0; i = i + 0.1) {
fgCol += inputTexture2.sample(quadSampler, uv + dir * i);
}
fgCol /= 10.0;
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessPendulumMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessPendulumMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "pendulumMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("pendulumMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,45 @@
//
// pendulumMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
#define PI 3.1415926
namespace rightDropMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 rightDropMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g);
float time = cos(uniform.factor * PI * 2.5) * exp(-uniform.factor);
float2 uv = fragmentInput.textureCoordinate2 + float2(0.5, 0.0) * time;
float2 dir = float2(0.1, 0.0) * time;
half4 fgCol = half4(0.0);
for(float i = 0.0; i < 1.0; i = i + 0.1) {
fgCol += inputTexture2.sample(quadSampler, uv + dir * i);
}
fgCol /= 10.0;
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessRightDropMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessRightDropMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "rightDropMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("rightDropMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,48 @@
//
// rotateMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace rotateInRightMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 rotateInRightMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g + uniform.roi.a * 0.5);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
float rotCorner = -(1. - uniform.factor) * 1.5707963;
float2 rot = float2(cos(rotCorner), sin(rotCorner));
uv = (uv - center + float2(uniform.roi.b * (1.0 - uniform.factor), 0.0)) * uniform.iResolution;
uv = float2(rot.x * uv.x + rot.y * uv.y, -rot.y * uv.x + rot.x * uv.y);
uv = uv / uniform.iResolution + center;
half4 fgCol = inputTexture2.sample(quadSampler, uv);
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessRotateMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessRotateInRightMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "rotateInRightMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseOut
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("rotateMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,48 @@
//
// rotateMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace rotateMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 rotateMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g + uniform.roi.a * 0.5);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
float rotCorner = uniform.factor * 25.1327408;
float2 rot = float2(cos(rotCorner), sin(rotCorner));
uv = (uv - center) * uniform.iResolution;
uv = float2(rot.x * uv.x + rot.y * uv.y, -rot.y * uv.x + rot.x * uv.y);
uv = uv / uniform.iResolution + center;
half4 fgCol = inputTexture2.sample(quadSampler, uv);
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessRotateMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessRotateMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "rotateMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseOut
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("rotateMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,36 @@
//
// slimZoomInMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace slimZoomInMotion {
typedef struct
{
float factor;
float4 roi;
} MotionUniform;
fragment half4 slimZoomInMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g + uniform.roi.a * 0.5);
float2 uv = fragmentInput.textureCoordinate2;
half4 fgCol = inputTexture2.sample(quadSampler, (uv - center) * (1.2 - uniform.factor * 0.2) + center);
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,37 @@
//
// MetalVideoProcessSlimZoomInMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessSlimZoomInMotion: MetalVideoProcessMotion {
public init() {
super.init(fragmentFunctionName: "slimZoomInMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseIn
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
debugPrint("slimZoomInMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,47 @@
//
// swirlMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace swirlMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 swirlMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g + uniform.roi.a * 0.5);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 uv = fragmentInput.textureCoordinate2;
float2 tempUv = uv - center;
float theta = atan2(tempUv.y, tempUv.x);
float r = length(tempUv);
theta = theta + r * 20.0 * (1. - uniform.factor);
uv = float2(r * cos(theta), r * sin(theta)) + center;
half4 fgCol = inputTexture2.sample(quadSampler, uv);
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessSwirlMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessSwirlMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "swirlMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseOut
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("rotateMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,45 @@
//
// pendulumMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
#define PI 3.1415926
namespace upDropMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 upDropMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.5, uniform.roi.g);
float time = cos(uniform.factor * PI * 2.5) * exp(-uniform.factor);
float2 uv = fragmentInput.textureCoordinate2 + float2(0.0, 0.5) * time;
float2 dir = float2(0.0, 0.1) * time;
half4 fgCol = half4(0.0);
for(float i = 0.0; i < 1.0; i = i + 0.1) {
fgCol += inputTexture2.sample(quadSampler, uv + dir * i);
}
fgCol /= 10.0;
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessUpDropMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessUpDropMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "upDropMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .linearInterpolation
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("upDropMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,53 @@
//
// upMoveInBlurIIMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace upMoveInBlurIIMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 upMoveInBlurIIMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b, uniform.roi.g + uniform.roi.a * 0.5);
float time = uniform.factor;
float2 uv = fragmentInput.textureCoordinate2;
float rotCorner = -(1. - time) * 1.5707963;
float2 rot = float2(cos(rotCorner), sin(rotCorner));
uv = (uv - center) * uniform.iResolution;
uv = float2(rot.x * uv.x + rot.y * uv.y, -rot.y * uv.x + rot.x * uv.y);
uv = uv / uniform.iResolution + center;
float2 dir = float2(uv - center) * 0.5 * (1. - time);
dir = -float2(rot.x * dir.x + rot.y * dir.y, -rot.y * dir.x + rot.x * dir.y) * 0.6;
half4 fgCol = half4(0.0);
for(float i = 0.0; i < 1.0; i = i + 0.1) {
fgCol += inputTexture2.sample(quadSampler, uv + dir * i);
}
fgCol /= 10.0;
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}
@@ -0,0 +1,46 @@
//
// MetalVideoProcessUpMoveInBlurIIMotion.swift
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
import UIKit
public class MetalVideoProcessUpMoveInBlurIIMotion: MetalVideoProcessMotion {
var iResolution: Position {
set {
uniformSettings["iResolution"] = newValue
}
get {
return uniformSettings["iResolution"]
}
}
public init() {
super.init(fragmentFunctionName: "upMoveInBlurIIMotion", numberOfInputs: 2, device: sharedMetalRenderingDevice)
self.timingType = .quadraticEaseOut
self.factor = 0.0
}
public override func newTextureAvailable(_ texture: Texture, fromSourceIndex: UInt, trackID: Int32) {
if let time = texture.timingStyle.timestamp?.asCMTime {
if time < timelineRange.start {
factor = 0.0
}
self.iResolution = Position(Float(texture.texture.width), Float(texture.texture.height))
debugPrint("upMoveInBlurMotion:", factor, " frameTime:", texture.frameTime)
super.newTextureAvailable(texture, fromSourceIndex: fromSourceIndex, trackID: trackID)
}
}
/// before fade in, we need the texture alpha keep to zero
/// - Parameter texture: texture
/// - Returns: result
public override func checkTimelineRange(with texture: Texture) -> (Bool) {
return true
}
}
@@ -0,0 +1,53 @@
//
// upMoveInBlurMotion.metal
// MetalVideoProcess
//
// Created by Ruanshengqiang Macro on 2020/7/21.
// Copyright © 2020 Ruanshengqiang Macro. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
namespace upMoveInBlurMotion {
typedef struct
{
float factor;
float4 roi;
float2 iResolution;
} MotionUniform;
fragment half4 upMoveInBlurMotion(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant MotionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler(mip_filter::linear, min_filter::linear, mag_filter::linear, address::clamp_to_zero);
half4 bgCol = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
float2 center = float2(uniform.roi.r + uniform.roi.b * 0.0, uniform.roi.g + uniform.roi.a * 0.5);
float time = clamp(uniform.factor * 2.0, 0.0, 1.0);
float2 uv = fragmentInput.textureCoordinate2;
float rotCorner = (1. - time) * 1.5707963;
float2 rot = float2(cos(rotCorner), sin(rotCorner));
uv = (uv - center) * uniform.iResolution;
uv = float2(rot.x * uv.x + rot.y * uv.y, -rot.y * uv.x + rot.x * uv.y);
uv = uv / uniform.iResolution + center;
float2 dir = float2(uv - center) * 0.5 * (1. - time);
dir = -float2(rot.x * dir.x + rot.y * dir.y, -rot.y * dir.x + rot.x * dir.y) * 0.3;
half4 fgCol = half4(0.0);
for(float i = 0.0; i < 1.0; i = i + 0.1) {
fgCol += inputTexture2.sample(quadSampler, uv + dir * i);
}
fgCol /= 10.0;
return half4(bgCol.rgb * (1. - fgCol.a) + fgCol.rgb, fgCol.a);
}
}

Some files were not shown because too many files have changed in this diff Show More