├── .gitignore
├── Maydayfile
├── Project.xcconfig
├── README.md
├── THVideoFaceSwapper.xcodeproj
├── project.pbxproj
└── xcshareddata
│ └── xcschemes
│ └── THVideoFaceSwapper.xcscheme
├── THVideoFaceSwapper.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── THVideoFaceSwapper.xcscmblueprint
│ └── xcschemes
│ └── THVideoFaceSwapper.xcscheme
├── addons.make
├── bin
└── data
│ ├── Default-568h@2x~iphone.png
│ ├── Default.png
│ ├── Default@2x.png
│ ├── Default@2x~ipad.png
│ ├── Default@2x~iphone.png
│ ├── Default~ipad.png
│ ├── Default~iphone.png
│ ├── Icon-72.png
│ ├── Icon-72@2x.png
│ ├── Icon.png
│ ├── Icon@2x.png
│ ├── faces
│ ├── 1535.jpg
│ ├── 95662.jpeg
│ ├── Barack_Obama,_official_photo_portrait,_111th_Congress.jpg
│ ├── Emma-Watson_2967465b.jpg
│ ├── Steve-Jobs-Stamp.jpg
│ ├── beard.jpg
│ ├── hairstyles-for-men-with-round-faces-best.jpg
│ ├── lebron-james-basketball-headshot-photo.jpg
│ ├── photo.jpg
│ └── pict.php.jpeg
│ ├── model
│ ├── face.con
│ ├── face.tracker
│ ├── face.tri
│ └── face2.tracker
│ └── shader
│ ├── cloneShader.frag
│ ├── cloneShader.vert
│ ├── maskBlurShader.frag
│ └── maskBlurShader.vert
├── ofxiOS-Info.plist
├── ofxiOS_Prefix.pch
├── openFrameworks
├── Project.xcconfig
├── ofxiOS-Info.plist
└── ofxiOS_Prefix.pch
└── src
├── Categories
├── UIImage+Decode.h
└── UIImage+Decode.m
├── Data
├── THFacesCollectionViewDataSource.h
└── THFacesCollectionViewDataSource.mm
├── Models
├── Clone.cpp
└── Clone.h
├── Resources
├── Default-568h@2x~iphone.png
├── Default.png
├── Default@2x.png
├── Icon.png
├── Icon@2x.png
└── MediaAssets.xcassets
│ ├── camera.imageset
│ ├── Camera-100.png
│ ├── Camera-64.png
│ └── Contents.json
│ └── close.imageset
│ ├── Close-100.png
│ ├── Close-64.png
│ └── Contents.json
├── View Controllers
├── THPhotoPickerViewController.h
└── THPhotoPickerViewController.mm
├── Views
├── Loading
│ ├── MBProgressHUD.h
│ └── MBProgressHUD.m
├── THFacePickerCollectionViewCell.h
├── THFacePickerCollectionViewCell.m
├── THFacesCollectionReusableView.h
└── THFacesCollectionReusableView.m
├── main.mm
├── ofApp.h
└── ofApp.mm
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | *.DS_Store
4 | build/
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | *.xccheckout
15 | *.moved-aside
16 | DerivedData
17 | *.hmap
18 | *.ipa
19 | *.xcuserstate
20 |
21 | # CocoaPods
22 | #
23 | # We recommend against adding the Pods directory to your .gitignore. However
24 | # you should judge for yourself, the pros and cons are mentioned at:
25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
26 | #
27 | #Pods/
28 |
--------------------------------------------------------------------------------
/Maydayfile:
--------------------------------------------------------------------------------
1 | xcode_proj 'THVideoFaceSwapper.xcodeproj'
2 |
3 | warning_regex 'TODO:', /\s+\/\/\s?TODO:/
4 |
--------------------------------------------------------------------------------
/Project.xcconfig:
--------------------------------------------------------------------------------
1 | //THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT.
2 | //THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED
3 | OF_PATH = ../../..
4 |
5 | //THIS HAS ALL THE HEADER AND LIBS FOR OF CORE
6 | #include "../../../libs/openFrameworksCompiled/project/ios/CoreOF.xcconfig"
7 |
8 | OTHER_LDFLAGS = $(OF_CORE_LIBS)
9 | HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS)
10 |
11 | ENABLE_BITCODE = NO
12 | COMPRESS_PNG_FILES = NO
13 | GCC_THUMB_SUPPORT = NO
14 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1
15 | TARGETED_DEVICE_FAMILY = 1
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # THVideoFaceSwapper
2 | Live video face swapping on iOS. Choose a face from the preloaded faces, or take one yourself!
3 |
4 | 
5 |
6 | #Building and Running the App
7 | The header search path for the openFramework libs should 3 levels up from this project (`../../..`). Saving this repo within `~/path/to/openFrameworks/apps/myApps/` and building and running the app will most likely work unless you've configured your openFrameoworks directory differently.
8 |
9 | If you want to save you this within a different directory that is not 3 levels below the openFrameworks root, then you will need to change the `OF_PATH` in the `Project.xcconfig` (not recommended).
10 |
11 | #Contributing
12 | Only alter the `src` Group within this project. If you add new Groups and/or files, please follow the directions below.
13 |
14 | The file structure for this project is maintained via [synx](https://github.com/venmo/synx). It mirrors the abstract Xcode file structure in Finder. When using on this project, there are a number of Groups to exclude due to openFrameworks needing an unaltered file structure. Once you've made your changes within the `src` folder, run `synx -e /openFrameworks -e /addons -e /libs -e /Products THVideoFaceSwapper.xcodeproj/`
15 | This will exclude all of the openFramework Groups.
16 |
17 | #Other Notes
18 | ###Adding Faces
19 | Faces go into `bin/data/faces/` as a JPG, JPEG or PNG
20 |
21 | ###Maydayfile
22 | Adds supplemental warnings and errors to your Xcode project via regex. For more info see [mayday](https://github.com/marklarr/mayday)
23 |
--------------------------------------------------------------------------------
/THVideoFaceSwapper.xcodeproj/xcshareddata/xcschemes/THVideoFaceSwapper.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/THVideoFaceSwapper.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/THVideoFaceSwapper.xcworkspace/xcshareddata/THVideoFaceSwapper.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "8852804AF21CBEE8B8D43E407C5315E4D57E430A",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "37526DA425E5430474A14FBFA1BC4525160B5FC1" : 0,
8 | "A4D0373C699384D3146D509694B516AB9C7B5AD6" : 0,
9 | "8852804AF21CBEE8B8D43E407C5315E4D57E430A" : 0
10 | },
11 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "E655A168-ACDA-4C10-BC7D-E16EAAF3DC54",
12 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
13 | "37526DA425E5430474A14FBFA1BC4525160B5FC1" : "..\/..\/addons\/ofxCv",
14 | "A4D0373C699384D3146D509694B516AB9C7B5AD6" : "..\/..\/addons\/ofxFaceTracker",
15 | "8852804AF21CBEE8B8D43E407C5315E4D57E430A" : "THVideoFaceSwapper\/"
16 | },
17 | "DVTSourceControlWorkspaceBlueprintNameKey" : "THVideoFaceSwapper",
18 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
19 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "THVideoFaceSwapper.xcworkspace",
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
21 | {
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:kylemcdonald\/ofxCv.git",
23 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
24 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "37526DA425E5430474A14FBFA1BC4525160B5FC1"
25 | },
26 | {
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:thehackerati\/THVideoFaceSwapper.git",
28 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
29 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8852804AF21CBEE8B8D43E407C5315E4D57E430A"
30 | },
31 | {
32 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:kylemcdonald\/ofxFaceTracker.git",
33 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "A4D0373C699384D3146D509694B516AB9C7B5AD6"
35 | }
36 | ]
37 | }
--------------------------------------------------------------------------------
/THVideoFaceSwapper.xcworkspace/xcshareddata/xcschemes/THVideoFaceSwapper.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/addons.make:
--------------------------------------------------------------------------------
1 | ofxCv
2 | ofxFaceTracker
3 | ofxOpenCv
4 |
--------------------------------------------------------------------------------
/bin/data/Default-568h@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default-568h@2x~iphone.png
--------------------------------------------------------------------------------
/bin/data/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default.png
--------------------------------------------------------------------------------
/bin/data/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default@2x.png
--------------------------------------------------------------------------------
/bin/data/Default@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default@2x~ipad.png
--------------------------------------------------------------------------------
/bin/data/Default@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default@2x~iphone.png
--------------------------------------------------------------------------------
/bin/data/Default~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default~ipad.png
--------------------------------------------------------------------------------
/bin/data/Default~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Default~iphone.png
--------------------------------------------------------------------------------
/bin/data/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Icon-72.png
--------------------------------------------------------------------------------
/bin/data/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Icon-72@2x.png
--------------------------------------------------------------------------------
/bin/data/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Icon.png
--------------------------------------------------------------------------------
/bin/data/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/Icon@2x.png
--------------------------------------------------------------------------------
/bin/data/faces/1535.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/1535.jpg
--------------------------------------------------------------------------------
/bin/data/faces/95662.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/95662.jpeg
--------------------------------------------------------------------------------
/bin/data/faces/Barack_Obama,_official_photo_portrait,_111th_Congress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/Barack_Obama,_official_photo_portrait,_111th_Congress.jpg
--------------------------------------------------------------------------------
/bin/data/faces/Emma-Watson_2967465b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/Emma-Watson_2967465b.jpg
--------------------------------------------------------------------------------
/bin/data/faces/Steve-Jobs-Stamp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/Steve-Jobs-Stamp.jpg
--------------------------------------------------------------------------------
/bin/data/faces/beard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/beard.jpg
--------------------------------------------------------------------------------
/bin/data/faces/hairstyles-for-men-with-round-faces-best.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/hairstyles-for-men-with-round-faces-best.jpg
--------------------------------------------------------------------------------
/bin/data/faces/lebron-james-basketball-headshot-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/lebron-james-basketball-headshot-photo.jpg
--------------------------------------------------------------------------------
/bin/data/faces/photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/photo.jpg
--------------------------------------------------------------------------------
/bin/data/faces/pict.php.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/bin/data/faces/pict.php.jpeg
--------------------------------------------------------------------------------
/bin/data/model/face.con:
--------------------------------------------------------------------------------
1 | n_connections: 61
2 | {
3 | 0 1
4 | 1 2
5 | 2 3
6 | 3 4
7 | 4 5
8 | 5 6
9 | 6 7
10 | 7 8
11 | 8 9
12 | 9 10
13 | 10 11
14 | 11 12
15 | 12 13
16 | 13 14
17 | 14 15
18 | 15 16
19 | 17 18
20 | 18 19
21 | 19 20
22 | 20 21
23 | 22 23
24 | 23 24
25 | 24 25
26 | 25 26
27 | 27 28
28 | 28 29
29 | 29 30
30 | 31 32
31 | 32 33
32 | 33 34
33 | 34 35
34 | 36 37
35 | 37 38
36 | 38 39
37 | 39 40
38 | 40 41
39 | 41 36
40 | 42 43
41 | 43 44
42 | 44 45
43 | 45 46
44 | 46 47
45 | 47 42
46 | 48 49
47 | 49 50
48 | 50 51
49 | 51 52
50 | 52 53
51 | 53 54
52 | 54 55
53 | 55 56
54 | 56 57
55 | 57 58
56 | 58 59
57 | 59 48
58 | 60 65
59 | 60 61
60 | 61 62
61 | 62 63
62 | 63 64
63 | 64 65
64 | }
65 |
--------------------------------------------------------------------------------
/bin/data/model/face.tri:
--------------------------------------------------------------------------------
1 | n_tri: 91
2 | {
3 | 20 21 23
4 | 21 22 23
5 | 0 1 36
6 | 15 16 45
7 | 0 17 36
8 | 16 26 45
9 | 17 18 37
10 | 25 26 44
11 | 17 36 37
12 | 26 44 45
13 | 18 19 38
14 | 24 25 43
15 | 18 37 38
16 | 25 43 44
17 | 19 20 38
18 | 23 24 43
19 | 20 21 39
20 | 22 23 42
21 | 20 38 39
22 | 23 42 43
23 | 21 22 27
24 | 21 27 39
25 | 22 27 42
26 | 27 28 42
27 | 27 28 39
28 | 28 42 47
29 | 28 39 40
30 | 1 36 41
31 | 15 45 46
32 | 1 2 41
33 | 14 15 46
34 | 28 29 40
35 | 28 29 47
36 | 2 40 41
37 | 14 46 47
38 | 2 29 40
39 | 14 29 47
40 | 2 3 29
41 | 13 14 29
42 | 29 30 31
43 | 29 30 35
44 | 3 29 31
45 | 13 29 35
46 | 30 32 33
47 | 30 33 34
48 | 30 31 32
49 | 30 34 35
50 | 3 4 31
51 | 12 13 35
52 | 4 5 48
53 | 11 12 54
54 | 5 6 48
55 | 10 11 54
56 | 6 48 59
57 | 10 54 55
58 | 6 7 59
59 | 9 10 55
60 | 7 58 59
61 | 9 55 56
62 | 8 57 58
63 | 8 56 57
64 | 7 8 58
65 | 8 9 56
66 | 4 31 48
67 | 12 35 54
68 | 31 48 49
69 | 35 53 54
70 | 31 49 50
71 | 35 52 53
72 | 31 32 50
73 | 34 35 52
74 | 32 33 50
75 | 33 34 52
76 | 33 50 51
77 | 33 51 52
78 | 48 49 60
79 | 49 60 50
80 | 50 60 61
81 | 50 51 61
82 | 51 52 61
83 | 61 62 52
84 | 52 53 62
85 | 53 54 62
86 | 54 55 63
87 | 55 56 63
88 | 56 63 64
89 | 56 57 64
90 | 64 65 57
91 | 57 58 65
92 | 58 59 65
93 | 48 59 65
94 | }
95 |
--------------------------------------------------------------------------------
/bin/data/shader/cloneShader.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform sampler2D src, srcBlur, dstBlur;
4 |
5 | varying vec2 texCoordVarying;
6 |
7 | void main() {
8 | vec2 pos = vec2(texCoordVarying.x, texCoordVarying.y);
9 | vec4 srcColorBlur = texture2D(srcBlur, pos);
10 | if(srcColorBlur.a > 0.){
11 |
12 | vec3 srcColor = texture2D(src, pos).rgb;
13 | vec4 dstColorBlur = texture2D(dstBlur, pos);
14 | vec3 offset = dstColorBlur.rgb - srcColorBlur.rgb;
15 |
16 | gl_FragColor = vec4(srcColor + offset, 1.);
17 |
18 | }
19 | else{
20 |
21 | gl_FragColor = vec4(0.);
22 |
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/bin/data/shader/cloneShader.vert:
--------------------------------------------------------------------------------
1 | // these are for the programmable pipeline system
2 | uniform mat4 modelViewProjectionMatrix;
3 | attribute vec4 position;
4 | attribute vec2 texcoord;
5 |
6 | // this is something we're creating for this shader
7 | varying vec2 texCoordVarying;
8 |
9 |
10 | void main()
11 | {
12 |
13 | texCoordVarying = texcoord;
14 |
15 | gl_Position = modelViewProjectionMatrix * position;
16 |
17 |
18 | }
--------------------------------------------------------------------------------
/bin/data/shader/maskBlurShader.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform sampler2D tex0, mask;
4 | uniform vec2 direction;
5 | uniform int k;
6 |
7 | varying vec2 texCoordVarying;
8 |
9 | void main() {
10 |
11 | vec2 pos = vec2(texCoordVarying.x, texCoordVarying.y); //gl_TexCoord[0].st;
12 | vec4 sum = texture2D(tex0, pos);
13 | int i;
14 | for(i=1;i= 0.5 && mask3.r >= 0.5){
25 |
26 |
27 | sum += texture2D(tex0, pos + curOffset) + texture2D(tex0, pos - curOffset);
28 |
29 | }
30 | else{
31 |
32 | break;
33 |
34 | }
35 | }
36 | int samples = 1 + (i-1)*2;
37 | sum /= float(samples);
38 |
39 | gl_FragColor = sum;
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/bin/data/shader/maskBlurShader.vert:
--------------------------------------------------------------------------------
1 | // these are for the programmable pipeline system
2 | uniform mat4 modelViewProjectionMatrix;
3 | attribute vec4 position;
4 | attribute vec2 texcoord;
5 |
6 | // this is something we're creating for this shader
7 | varying vec2 texCoordVarying;
8 |
9 |
10 | void main()
11 | {
12 |
13 | texCoordVarying = texcoord;
14 |
15 | gl_Position = modelViewProjectionMatrix * position;
16 |
17 |
18 | }
--------------------------------------------------------------------------------
/ofxiOS-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIStatusBarHidden
6 |
7 | UIViewControllerBasedStatusBarAppearance
8 |
9 | CFBundleDevelopmentRegion
10 | English
11 | CFBundleDisplayName
12 | ${PRODUCT_NAME}
13 | CFBundleExecutable
14 | ${EXECUTABLE_NAME}
15 | CFBundleIconFile
16 |
17 | CFBundleIcons
18 |
19 | CFBundlePrimaryIcon
20 |
21 | CFBundleIconFiles
22 |
23 | Icon.png
24 | Icon@2x.png
25 |
26 |
27 |
28 | CFBundleIdentifier
29 | ${PRODUCT_NAME:identifier}
30 | CFBundleInfoDictionaryVersion
31 | 6.0
32 | CFBundleName
33 | ${PRODUCT_NAME}
34 | CFBundlePackageType
35 | APPL
36 | CFBundleShortVersionString
37 | 1.0
38 | CFBundleSignature
39 | ????
40 | CFBundleVersion
41 | 1.0
42 | LSRequiresIPhoneOS
43 |
44 | UIApplicationExitsOnSuspend
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ofxiOS_Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'iPhone' target in the 'iPhone' project
3 | //
4 |
5 | #ifdef __OBJC__
6 | #import
7 | #import
8 | #endif
9 |
--------------------------------------------------------------------------------
/openFrameworks/Project.xcconfig:
--------------------------------------------------------------------------------
1 | //THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT.
2 | //THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED
3 | OF_PATH = ../../..
4 |
5 | //THIS HAS ALL THE HEADER AND LIBS FOR OF CORE
6 | #include "../../../libs/openFrameworksCompiled/project/ios/CoreOF.xcconfig"
7 |
8 | OTHER_LDFLAGS = $(OF_CORE_LIBS)
9 | HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS)
10 |
11 | COMPRESS_PNG_FILES = NO
12 | GCC_THUMB_SUPPORT = NO
13 | IPHONEOS_DEPLOYMENT_TARGET = 3.1
14 | TARGETED_DEVICE_FAMILY = 1
15 |
--------------------------------------------------------------------------------
/openFrameworks/ofxiOS-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIStatusBarHidden
6 |
7 | UIViewControllerBasedStatusBarAppearance
8 |
9 | CFBundleDevelopmentRegion
10 | English
11 | CFBundleDisplayName
12 | ${PRODUCT_NAME}
13 | CFBundleExecutable
14 | ${EXECUTABLE_NAME}
15 | CFBundleIconFile
16 |
17 | CFBundleIcons
18 |
19 | CFBundlePrimaryIcon
20 |
21 | CFBundleIconFiles
22 |
23 | Icon.png
24 | Icon@2x.png
25 |
26 |
27 |
28 | CFBundleIdentifier
29 | ${PRODUCT_NAME:identifier}
30 | CFBundleInfoDictionaryVersion
31 | 6.0
32 | CFBundleName
33 | ${PRODUCT_NAME}
34 | CFBundlePackageType
35 | APPL
36 | CFBundleShortVersionString
37 | 1.0
38 | CFBundleSignature
39 | ????
40 | CFBundleVersion
41 | 1.0
42 | LSRequiresIPhoneOS
43 |
44 | UIApplicationExitsOnSuspend
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/openFrameworks/ofxiOS_Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'iPhone' target in the 'iPhone' project
3 | //
4 |
5 | #ifdef __OBJC__
6 | #import
7 | #import
8 | #endif
9 |
--------------------------------------------------------------------------------
/src/Categories/UIImage+Decode.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Decode.h
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/22/15.
6 | //
7 | //
8 |
9 | @interface UIImage (Decode)
10 |
11 | - (UIImage *)decodedImage;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/Categories/UIImage+Decode.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Decode.m
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/22/15.
6 | //
7 | //
8 |
9 | #import "UIImage+Decode.h"
10 |
11 | @implementation UIImage (Decode)
12 |
13 | - (UIImage *)decodedImage
14 | {
15 | CGImageRef imageRef = self.CGImage;
16 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
17 | CGContextRef context = CGBitmapContextCreate(NULL,
18 | CGImageGetWidth(imageRef),
19 | CGImageGetHeight(imageRef),
20 | 8,
21 | // Just always return width * 4 will be enough
22 | CGImageGetWidth(imageRef) * 4,
23 | // System only supports RGB, set explicitly
24 | colorSpace,
25 | // Makes system don't need to do extra conversion when displayed.
26 | kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
27 | CGColorSpaceRelease(colorSpace);
28 | if (!context) return nil;
29 |
30 | CGRect rect = (CGRect){CGPointZero,{static_cast(CGImageGetWidth(imageRef)), static_cast(CGImageGetHeight(imageRef))}};
31 | CGContextDrawImage(context, rect, imageRef);
32 | CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
33 | CGContextRelease(context);
34 |
35 | UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef scale:self.scale orientation:self.imageOrientation];
36 | CGImageRelease(decompressedImageRef);
37 | return decompressedImage;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/src/Data/THFacesCollectionViewDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // THFacesCollectionViewDataSource.h
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/27/15.
6 | //
7 | //
8 |
9 | #include
10 |
11 | @interface THFacesCollectionViewDataSource : NSObject
12 |
13 | - (instancetype)initWithCollectionView:(UICollectionView *)collectionView;
14 |
15 | - (NSInteger)numberOfItemsToDelete;
16 | - (void)addIndexPathToDelete:(NSIndexPath *)indexPath;
17 | - (void)removeIndexPathToDelete:(NSIndexPath *)indexPath;
18 | - (void)deleteSelectedItems:(void (^)(void))completion;
19 |
20 | - (NSString *)savedImageNameAtIndexPath:(NSIndexPath *)indexPath;
21 | - (UIImage *)imageFromDocumentsDirectoryNamed:(NSString *)name;
22 |
23 | - (void)loadFace:(UIImage *)image saveImage:(BOOL)save withCompletion:(void (^)(void))completion;
24 |
25 | - (void)resetCellStates;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/src/Data/THFacesCollectionViewDataSource.mm:
--------------------------------------------------------------------------------
1 | //
2 | // THFacesCollectionViewDataSource.m
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/27/15.
6 | //
7 | //
8 |
9 | #include "ofApp.h"
10 |
11 | #import "THFacesCollectionViewDataSource.h"
12 | #import "THFacePickerCollectionViewCell.h"
13 | #import "THFacesCollectionReusableView.h"
14 |
15 | #import "UIImage+Decode.h"
16 |
17 | static NSString *kTHCellReuseIdentifier = @"cell";
18 | static NSString *kTHSupplementaryHeaderViewReuseIdentifier = @"supplementaryHeaderView";
19 |
20 | static NSString * const kTHDocumentsDirectoryPath = ofxStringToNSString(ofxiOSGetDocumentsDirectory());
21 | static NSString * const kTHAlphanumericCharacters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
22 | static NSString * const kTHPreInstalledFacesHeaderTitle = @"Pre-Installed Faces";
23 | static NSString * const kTHSavedFacesHeaderTitle = @"Saved Faces";
24 |
25 | static const string kTHSavedImagesExtension = ".png";
26 |
27 | @interface THFacesCollectionViewDataSource ()
28 | {
29 | ofApp *mainApp;
30 | }
31 |
32 | @property (nonatomic) UICollectionView *collectionView;
33 | @property (nonatomic) NSMutableArray *savedFaces;
34 | @property (nonatomic) NSMutableSet *indexPathsToDelete;
35 |
36 | @end
37 |
38 | @implementation THFacesCollectionViewDataSource
39 |
40 | - (instancetype)initWithCollectionView:(UICollectionView *)collectionView
41 | {
42 | self = [super init];
43 | if ( self ) {
44 | mainApp = (ofApp *)ofGetAppPtr();
45 | _collectionView = collectionView;
46 | [_collectionView registerClass:[THFacePickerCollectionViewCell class] forCellWithReuseIdentifier:kTHCellReuseIdentifier];
47 | [_collectionView registerClass:[THFacesCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kTHSupplementaryHeaderViewReuseIdentifier];
48 |
49 | _savedFaces = (NSMutableArray *)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:kTHDocumentsDirectoryPath error:nil] mutableCopy];
50 | _indexPathsToDelete = [[NSMutableSet alloc] init];
51 | }
52 | return self;
53 | }
54 |
55 | - (void)dealloc
56 | {
57 | _collectionView = nil;
58 | _savedFaces = nil;
59 | _indexPathsToDelete = nil;
60 | mainApp = NULL;
61 | }
62 |
63 | #pragma mark - Public
64 |
65 | - (NSInteger)numberOfItemsToDelete
66 | {
67 | return self.indexPathsToDelete.count;
68 | }
69 |
70 | - (void)addIndexPathToDelete:(NSIndexPath *)indexPath
71 | {
72 | if ( ![self.indexPathsToDelete containsObject:indexPath] ) {
73 | [self.indexPathsToDelete addObject:indexPath];
74 | }
75 | }
76 |
77 | - (void)removeIndexPathToDelete:(NSIndexPath *)indexPath
78 | {
79 | if ( [self.indexPathsToDelete containsObject:indexPath] ) {
80 | [self.indexPathsToDelete removeObject:indexPath];
81 | }
82 | }
83 |
84 | - (void)deleteSelectedItems:(void (^)(void))completion
85 | {
86 | [self.collectionView performBatchUpdates:^{
87 |
88 | NSMutableArray *fileNamesToRemove = [NSMutableArray array];
89 | NSError *err;
90 | for (NSIndexPath *path in self.indexPathsToDelete) {
91 |
92 | THFacePickerCollectionViewCell *cell = (THFacePickerCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:path];
93 | [cell highlightSelected:NO];
94 |
95 | [fileNamesToRemove addObject:[self.savedFaces objectAtIndex:path.row]];
96 | NSString *fileToDelete = [kTHDocumentsDirectoryPath stringByAppendingPathComponent:[self.savedFaces objectAtIndex:path.row]];
97 |
98 | if ( ![[NSFileManager defaultManager] removeItemAtPath:fileToDelete error:&err] ) {
99 |
100 | UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:@"File Save Error"
101 | message:err.localizedDescription
102 | delegate:nil
103 | cancelButtonTitle:@"Ok"
104 | otherButtonTitles:nil, nil];
105 | [errorAlert show];
106 | }
107 | }
108 |
109 | [self.savedFaces removeObjectsInArray:fileNamesToRemove];
110 | [self.collectionView deleteItemsAtIndexPaths:[self.indexPathsToDelete allObjects]];
111 |
112 | } completion:^(BOOL finished){
113 | [self.indexPathsToDelete removeAllObjects];
114 | if ( completion ) {
115 | completion();
116 | }
117 | }];
118 | }
119 |
120 | - (NSString *)savedImageNameAtIndexPath:(NSIndexPath *)indexPath
121 | {
122 | return self.savedFaces[indexPath.row];
123 | }
124 |
125 | - (UIImage *)imageFromDocumentsDirectoryNamed:(NSString *)name
126 | {
127 | NSString *imagePath = [kTHDocumentsDirectoryPath stringByAppendingPathComponent:name];
128 | return [UIImage imageWithContentsOfFile:imagePath];
129 | }
130 |
131 | - (void)loadFace:(UIImage *)image saveImage:(BOOL)save withCompletion:(void (^)(void))completion
132 | {
133 | NSLog(@"LOADING FACE");
134 | dispatch_async(dispatch_queue_create("imageLoadingQueue", NULL), ^{
135 |
136 | ofImage pickedImage;
137 | ofxiOSUIImageToOFImage(image, pickedImage);
138 | pickedImage.rotate90(1);
139 |
140 | if ( save ) {
141 | NSString *newFileName = [self randomString];
142 | while ( [self.savedFaces containsObject:newFileName] ) { // ensures that we'll never over write a previous image (highly unlikely anyways)
143 | newFileName = [self randomString];
144 | }
145 | string cStringSavedFaceName = ofxNSStringToString(newFileName) + kTHSavedImagesExtension;
146 | // Save image to documents directory
147 | pickedImage.saveImage(ofxiOSGetDocumentsDirectory() + cStringSavedFaceName);
148 | [self.savedFaces addObject:ofxStringToNSString(cStringSavedFaceName)];
149 | }
150 |
151 | pickedImage.setImageType(OF_IMAGE_COLOR); // set the type AFTER we save image to documents
152 |
153 | dispatch_async(dispatch_get_main_queue(), ^{
154 |
155 | mainApp->loadOFImage(pickedImage);
156 | mainApp->setupCam([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
157 |
158 | [self.collectionView reloadSections:[[NSIndexSet alloc] initWithIndex:1]];
159 |
160 | if ( completion ) {
161 | completion();
162 | }
163 | });
164 | });
165 | }
166 |
167 | - (void)resetCellStates
168 | {
169 | for (NSIndexPath *path in self.indexPathsToDelete) {
170 | THFacePickerCollectionViewCell *cell = (THFacePickerCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:path];
171 | [cell highlightSelected:NO];
172 | }
173 |
174 | [self.indexPathsToDelete removeAllObjects];
175 | }
176 |
177 | #pragma mark - Private
178 |
179 | - (NSString *)randomString {
180 | const NSInteger alphanumericLettersCount = [kTHAlphanumericCharacters length];
181 |
182 | NSMutableString *randomString = [[NSMutableString alloc] init];
183 | for (int i = 0; i < 10; i++) {
184 | [randomString appendFormat: @"%C", [kTHAlphanumericCharacters characterAtIndex: arc4random_uniform(alphanumericLettersCount)]];
185 | }
186 |
187 | return randomString;
188 | }
189 |
190 | UIImage * uiimageFromOFImage(ofImage inputImage)
191 | {
192 | int width = inputImage.getWidth();
193 | int height = inputImage.getHeight();
194 | float pixelForChannel = 3;
195 | CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, inputImage.getPixels(), width*height*pixelForChannel, NULL);
196 | CGImageRef imageRef = CGImageCreate(width, height, 8, 24, pixelForChannel*width, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault, provider, NULL, NO, kCGRenderingIntentDefault);
197 | UIImage *pngImge = [UIImage imageWithCGImage:imageRef];
198 | NSData *imageData = UIImagePNGRepresentation(pngImge);
199 | UIImage *output = [UIImage imageWithData:imageData];
200 | return output;
201 | }
202 |
203 | #pragma mark - UICollectionView Datasource
204 |
205 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
206 | {
207 | return 2;
208 | }
209 |
210 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
211 | {
212 | if ( section == 0 ) {
213 | return mainApp->faces.size();
214 | }
215 | else {
216 | return self.savedFaces.count;
217 | }
218 | }
219 |
220 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
221 | {
222 | THFacesCollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kTHSupplementaryHeaderViewReuseIdentifier forIndexPath:indexPath];
223 |
224 | if ( indexPath.section == 0 ) {
225 | headerView.title = kTHPreInstalledFacesHeaderTitle;
226 | }
227 | else {
228 | headerView.title = kTHSavedFacesHeaderTitle;
229 | }
230 |
231 | return headerView;
232 | }
233 |
234 | -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
235 | {
236 | THFacePickerCollectionViewCell *cell = (THFacePickerCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kTHCellReuseIdentifier forIndexPath:indexPath];
237 |
238 | cell.currentIndexPath = indexPath;
239 | [cell clearImage];
240 | [cell startLoading];
241 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
242 |
243 | UIImage *faceImage;
244 | if ( indexPath.section == 0 ) {
245 | ofImage preInstalledImage;
246 | preInstalledImage.loadImage(mainApp->faces.getPath(indexPath.row));
247 | faceImage = uiimageFromOFImage(preInstalledImage);
248 | }
249 | else {
250 |
251 | faceImage = [[UIImage imageWithContentsOfFile:[kTHDocumentsDirectoryPath stringByAppendingPathComponent:[self.savedFaces objectAtIndex:indexPath.row]]] decodedImage];
252 | }
253 |
254 | dispatch_async(dispatch_get_main_queue(), ^{
255 | [cell setFaceWithImage:faceImage atIndexPath:indexPath];
256 | [cell stopLoading];
257 | });
258 | });
259 |
260 | return cell;
261 | }
262 |
263 | @end
264 |
--------------------------------------------------------------------------------
/src/Models/Clone.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Clone.cpp
3 | // FaceSubstitutionCamera
4 | //
5 | // Created by James Lilard on 2014/10/20.
6 | //
7 | //
8 |
9 | #include "Clone.h"
10 |
11 |
12 | void Clone::setup(int width, int height) {
13 | ofFbo::Settings settings;
14 | settings.width = width;
15 | settings.height = height;
16 |
17 | buffer.allocate(settings);
18 | srcBlur.allocate(settings);
19 | dstBlur.allocate(settings);
20 |
21 | maskBlurShader.load("shader/maskBlurShader");
22 | cloneShader.load("shader/cloneShader");
23 |
24 | strength = 0;
25 | }
26 |
27 | void Clone::maskedBlur(ofTexture& tex, ofTexture& mask, ofFbo& result) {
28 | int k = strength;
29 |
30 | buffer.begin();
31 | maskBlurShader.begin();
32 | maskBlurShader.setUniformTexture("tex0", tex, 1);
33 | maskBlurShader.setUniformTexture("mask", mask, 2);
34 | maskBlurShader.setUniform2f("direction", 1./(tex.getWidth()*2), 0.);
35 | maskBlurShader.setUniform1i("k", k);
36 | tex.draw(0, 0);
37 | maskBlurShader.end();
38 | buffer.end();
39 |
40 | result.begin();
41 | maskBlurShader.begin();
42 | maskBlurShader.setUniformTexture("tex0", buffer, 1);
43 | maskBlurShader.setUniformTexture("mask", mask, 2);
44 | maskBlurShader.setUniform2f("direction", 0., 1./(buffer.getHeight()));
45 | maskBlurShader.setUniform1i("k", k);
46 | buffer.draw(0, 0);
47 | maskBlurShader.end();
48 | buffer.draw(0, 0);
49 | result.end();
50 | }
51 |
52 | void Clone::setStrength(int strength) {
53 | this->strength = strength;
54 | }
55 |
56 | void Clone::update(ofTexture& src, ofTexture& dst, ofTexture& mask) {
57 | maskedBlur(src, mask, srcBlur);
58 | maskedBlur(dst, mask, dstBlur);
59 |
60 | buffer.begin();
61 | ofPushStyle();
62 | ofEnableAlphaBlending();
63 | dst.draw(0, 0);
64 | cloneShader.begin();
65 | cloneShader.setUniformTexture("src", src, 1);
66 | cloneShader.setUniformTexture("srcBlur", srcBlur, 2);
67 | cloneShader.setUniformTexture("dstBlur", dstBlur, 3);
68 | dst.draw(0, 0);
69 | cloneShader.end();
70 | ofDisableAlphaBlending();
71 | ofPopStyle();
72 | buffer.end();
73 | }
74 |
75 | void Clone::draw(float x, float y) {
76 | buffer.draw(x, y);
77 | }
78 |
79 |
80 | void Clone::debugShader(ofTexture &tex, ofTexture &mask){
81 |
82 | debugFbo.allocate(buffer.getWidth(), buffer.getHeight());
83 | debugResultFbo.allocate(buffer.getWidth(), buffer.getHeight());
84 | ofShader debugShader;
85 | debugShader.load("shader/maskBlurShader");
86 |
87 | setStrength(16);
88 |
89 |
90 | debugFbo.begin();
91 |
92 | // debugShader.begin();
93 | //
94 | // //maskBlurShader.setUniformTexture("tex", tex, 1);
95 | // debugShader.setUniformTexture("tex0", tex, 1);
96 | // debugShader.setUniformTexture("mask", mask, 2);
97 | // debugShader.setUniform2f("direction", 0, 1);
98 | // debugShader.setUniform1i("k", strength);
99 | // mask.draw(0, 0);
100 | //
101 | // debugShader.end();
102 | maskBlurShader.begin();
103 | maskBlurShader.setUniformTexture("tex0", tex, 1);
104 | maskBlurShader.setUniformTexture("mask", mask, 2);
105 | //maskBlurShader.setUniform2f("direction", 0., 1./tex.getHeight());
106 | maskBlurShader.setUniform2f("direction", 1./tex.getWidth(), 0.);
107 | maskBlurShader.setUniform1i("k", strength);
108 | tex.draw(0, 0);
109 | maskBlurShader.end();
110 |
111 |
112 | debugFbo.end();
113 |
114 | // debugResultFbo.begin();
115 | //
116 | // maskBlurShader.setUniformTexture("tex0", debugFbo, 1);
117 | // maskBlurShader.setUniformTexture("mask", mask, 2);
118 | // maskBlurShader.setUniform2f("direction", 0., 1./tex.getHeight());
119 | // maskBlurShader.setUniform1i("k", strength);
120 | // debugFbo.draw(0, 0);
121 | //
122 | // debugResultFbo.end();
123 |
124 |
125 | }
--------------------------------------------------------------------------------
/src/Models/Clone.h:
--------------------------------------------------------------------------------
1 | //
2 | // Clone.h
3 | // FaceSubstitutionCamera
4 | //
5 | // Created by James Lilard on 2014/10/20.
6 | //
7 | //
8 |
9 | #ifndef __FaceSubstitutionCamera__Clone__
10 | #define __FaceSubstitutionCamera__Clone__
11 |
12 | #include "ofMain.h"
13 |
14 | class Clone{
15 |
16 | public:
17 | void setup(int width, int height);
18 | void setStrength(int strength);
19 | void update(ofTexture &src, ofTexture &dst, ofTexture &mask);
20 | void draw(float x, float y);
21 |
22 |
23 | //protected:
24 | void maskedBlur(ofTexture &tex, ofTexture &mask, ofFbo &result);
25 | ofFbo buffer, srcBlur, dstBlur;
26 | ofShader maskBlurShader, cloneShader;
27 | int strength;
28 |
29 |
30 | ofFbo debugFbo,debugResultFbo;
31 | void debugShader(ofTexture &tex, ofTexture &mask);
32 |
33 | };
34 |
35 | #endif /* defined(__FaceSubstitutionCamera__Clone__) */
36 |
--------------------------------------------------------------------------------
/src/Resources/Default-568h@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/Default-568h@2x~iphone.png
--------------------------------------------------------------------------------
/src/Resources/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/Default.png
--------------------------------------------------------------------------------
/src/Resources/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/Default@2x.png
--------------------------------------------------------------------------------
/src/Resources/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/Icon.png
--------------------------------------------------------------------------------
/src/Resources/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/Icon@2x.png
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/camera.imageset/Camera-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/MediaAssets.xcassets/camera.imageset/Camera-100.png
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/camera.imageset/Camera-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/MediaAssets.xcassets/camera.imageset/Camera-64.png
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "Camera-64.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "Camera-100.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/close.imageset/Close-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/MediaAssets.xcassets/close.imageset/Close-100.png
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/close.imageset/Close-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackerati/THVideoFaceSwapper/118c3a8cad00b000aa1863238de28b0ec3e96dd8/src/Resources/MediaAssets.xcassets/close.imageset/Close-64.png
--------------------------------------------------------------------------------
/src/Resources/MediaAssets.xcassets/close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "Close-64.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "Close-100.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/src/View Controllers/THPhotoPickerViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // THPhotoPickerViewController.h
3 | // videoApp
4 | //
5 | // Created by Clayton Rieck on 4/8/15.
6 | //
7 | //
8 |
9 | #include
10 |
11 | @interface THPhotoPickerViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/View Controllers/THPhotoPickerViewController.mm:
--------------------------------------------------------------------------------
1 | //
2 | // FSPhotoPickerViewController.m
3 | // videoApp
4 | //
5 | // Created by Clayton Rieck on 4/8/15.
6 | //
7 | //
8 |
9 | #import "THPhotoPickerViewController.h"
10 | #include "ofApp.h"
11 |
12 | #import "THFacePickerCollectionViewCell.h"
13 | #import "THFacesCollectionViewDataSource.h"
14 | #import "MBProgressHUD.h"
15 |
16 | static NSString * const kTHFacePickerViewControllerTitle = @"Face Selector";
17 | static NSString * const kTHDeleteButtonSingleItemTitle = @"Delete Item";
18 | static NSString * const kTHDeleteButtonMultiItemTitle = @"Delete Items";
19 |
20 | static NSArray * const kTHLoadingDetails = @[@"You're gonna look great!", @"Ooo how handsome", @"Your alias is on the way!", @"Better than Mrs. Doubtfire"];
21 |
22 | static const CGSize kTHCellSize = (CGSize){100.0f, 100.0f};
23 | static const CGFloat kTHItemSpacing = 2.0f;
24 | static const UIEdgeInsets kTHCollectionViewEdgeInsets = (UIEdgeInsets){0.0f, 8.0f, 8.0f, 8.0f};
25 |
26 | @interface THPhotoPickerViewController ()
27 |
32 | {
33 | ofApp *mainApp;
34 | }
35 |
36 | @property (nonatomic) UICollectionView *facesCollectionView;
37 | @property (nonatomic) THFacesCollectionViewDataSource *dataSource;
38 | @property (nonatomic) UIButton *deleteButton;
39 | @property (nonatomic) UIImage *takenPhoto;
40 |
41 | @end
42 |
43 | @implementation THPhotoPickerViewController
44 |
45 | - (instancetype)init
46 | {
47 | self = [super init];
48 | if ( self ) {
49 | mainApp = (ofApp *)ofGetAppPtr();
50 | self.title = kTHFacePickerViewControllerTitle;
51 | }
52 | return self;
53 | }
54 |
55 | - (void)setupMenuButtons
56 | {
57 | UIBarButtonItem *dismissButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"close"] style:UIBarButtonItemStylePlain target:self action:@selector(dismissVC)];
58 | self.navigationItem.leftBarButtonItem = dismissButton;
59 |
60 | UIBarButtonItem *cameraButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"camera"] style:UIBarButtonItemStylePlain target:self action:@selector(presentCameraPicker)];
61 | self.navigationItem.rightBarButtonItem = cameraButton;
62 | }
63 |
64 | - (void)setupDeleteButton
65 | {
66 | const CGFloat navBarHeight = self.navigationController.navigationBar.frame.size.height;
67 |
68 | _deleteButton = [[UIButton alloc] initWithFrame:self.navigationController.navigationBar.frame];
69 | [_deleteButton addTarget:self action:@selector(deleteSelectedItems) forControlEvents:UIControlEventTouchUpInside];
70 | _deleteButton.backgroundColor = [UIColor colorWithRed:0.9f green:0.3f blue:0.26f alpha:1.0f];
71 | _deleteButton.transform = CGAffineTransformMakeTranslation(0.0f, -navBarHeight);
72 | _deleteButton.hidden = YES;
73 | _deleteButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
74 | [self.navigationController.navigationBar addSubview:_deleteButton];
75 | }
76 |
77 | - (void)setupCollectionView
78 | {
79 | const CGRect collectionViewFrame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height);
80 | const CGSize headerViewSize = CGSizeMake(self.view.frame.size.width, 30.0f);
81 |
82 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
83 | flowLayout.headerReferenceSize = headerViewSize;
84 | flowLayout.itemSize = kTHCellSize;
85 | flowLayout.minimumInteritemSpacing = kTHItemSpacing;
86 | flowLayout.minimumLineSpacing = kTHItemSpacing;
87 |
88 | _facesCollectionView = [[UICollectionView alloc] initWithFrame:collectionViewFrame collectionViewLayout:flowLayout];
89 | _facesCollectionView.backgroundColor = [UIColor whiteColor];
90 | _facesCollectionView.contentInset = kTHCollectionViewEdgeInsets;
91 | _facesCollectionView.delegate = self;
92 | _dataSource = [[THFacesCollectionViewDataSource alloc] initWithCollectionView:_facesCollectionView];
93 | _facesCollectionView.dataSource = _dataSource;
94 | _facesCollectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
95 | [self.view addSubview:_facesCollectionView];
96 |
97 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
98 | longPress.minimumPressDuration = 0.5f;
99 | longPress.numberOfTouchesRequired = 1;
100 | longPress.delaysTouchesBegan = YES; // prevent didHighlightItemAtIndexPath from being called first
101 | [_facesCollectionView addGestureRecognizer:longPress];
102 | }
103 |
104 | - (void)dealloc
105 | {
106 | _facesCollectionView = nil;
107 | mainApp = NULL;
108 | }
109 |
110 | - (void)loadView
111 | {
112 | self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
113 | self.view.backgroundColor = [UIColor whiteColor];
114 |
115 | [self setupMenuButtons];
116 | [self setupCollectionView];
117 | }
118 |
119 | - (void)viewDidAppear:(BOOL)animated
120 | {
121 | [super viewDidAppear:animated];
122 | [self setupDeleteButton]; // prevents bug where button doesn't appear after taking a photo and trying to delete a saved photo
123 | }
124 |
125 | #pragma mark - Private
126 |
127 | - (void)dismissVC
128 | {
129 | [self.dataSource resetCellStates];
130 |
131 | mainApp->setupCam([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width);
132 | [self.navigationController dismissViewControllerAnimated:YES completion:^{
133 | [MBProgressHUD hideHUDForView:self.view animated:YES];
134 | }];
135 | }
136 |
137 | - (void)presentCameraPicker
138 | {
139 | UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
140 | imagePicker.delegate = self;
141 | imagePicker.allowsEditing = NO;
142 | imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
143 |
144 | [self.navigationController presentViewController:imagePicker animated:YES completion:nil];
145 | }
146 |
147 | - (NSString *)randomLoadingDetail
148 | {
149 | const NSInteger lowerBound = 0;
150 | const NSInteger upperBound = kTHLoadingDetails.count;
151 | const NSInteger randomPosition = lowerBound + arc4random() % (upperBound - lowerBound);
152 | return kTHLoadingDetails[randomPosition];
153 | }
154 |
155 | - (void)showLoadingHUD
156 | {
157 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
158 | hud.mode = MBProgressHUDModeIndeterminate;
159 | hud.labelText = @"Loading Face";
160 | hud.detailsLabelText = [self randomLoadingDetail];
161 | }
162 |
163 | - (void)showDeleteButton:(BOOL)show
164 | {
165 | CGAffineTransform targetTransform;
166 | if ( show ) {
167 | [self.deleteButton setHidden:!show];
168 | targetTransform = CGAffineTransformMakeTranslation(0.0f, 0.0f);
169 | }
170 | else {
171 | targetTransform = CGAffineTransformMakeTranslation(0.0f, -self.navigationController.navigationBar.frame.size.height);
172 | }
173 |
174 | [UIView animateWithDuration:0.1f
175 | animations:^{
176 | self.deleteButton.transform = targetTransform;
177 | }
178 | completion:^(BOOL finished){
179 | [self.deleteButton setHidden:!show];
180 | }];
181 | }
182 |
183 | #pragma mark - UIAlertView Delegate
184 |
185 | - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
186 | {
187 | BOOL save = NO;
188 | if ( buttonIndex == 1 ) {
189 | save = YES;
190 | }
191 |
192 | [self.navigationController dismissViewControllerAnimated:YES completion:^{
193 | [self showLoadingHUD];
194 | [self.dataSource loadFace:self.takenPhoto saveImage:save withCompletion:^{
195 | self.takenPhoto = nil;
196 | [self dismissVC];
197 | }];
198 | }];
199 | }
200 |
201 | #pragma mark - UIImagePickerController Delegate
202 |
203 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
204 | {
205 | self.takenPhoto = (UIImage *)[info[UIImagePickerControllerOriginalImage] copy]; // need to copy or else you'll try to load the reference to the image which will be deallocated outside of this scope
206 |
207 | mainApp->cam.close();
208 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Save Photo For Later?"
209 | message:@"Saving will allow you to use this face later on"
210 | delegate:self
211 | cancelButtonTitle:@"No Thanks"
212 | otherButtonTitles:@"Right On!", nil];
213 | [alertView show];
214 | }
215 |
216 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
217 | {
218 | [picker dismissViewControllerAnimated:YES completion:nil];
219 | }
220 |
221 | #pragma mark - UICollectionView Delegate
222 |
223 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
224 | {
225 | [self showLoadingHUD];
226 |
227 | dispatch_async(dispatch_queue_create("imageLoadingQueue", NULL), ^{
228 | dispatch_async(dispatch_get_main_queue(), ^{
229 | if ( indexPath.section == 0 ) {
230 | mainApp->loadFace(mainApp->faces.getPath(indexPath.row));
231 | }
232 | else {
233 | NSString *imageName = [self.dataSource savedImageNameAtIndexPath:indexPath];
234 | ofImage savedImage;
235 | ofxiOSUIImageToOFImage([self.dataSource imageFromDocumentsDirectoryNamed:imageName], savedImage);
236 | mainApp->loadOFImage(savedImage);
237 | }
238 |
239 | [self dismissVC];
240 | });
241 | });
242 | }
243 |
244 | #pragma mark - UIGestureRecognizer Selectors
245 |
246 | - (void)longPress:(UIGestureRecognizer *)gesture
247 | {
248 | switch ( gesture.state ) {
249 | case UIGestureRecognizerStateBegan: {
250 | const CGPoint gesturePoint = [gesture locationInView:self.facesCollectionView];
251 | NSIndexPath *pointIndexPath = [self.facesCollectionView indexPathForItemAtPoint:gesturePoint];
252 |
253 | if ( pointIndexPath ) {
254 |
255 | if ( pointIndexPath.section == 1 ) { // should only be able to delete saved images taken via camera
256 |
257 | THFacePickerCollectionViewCell *selectedCell = (THFacePickerCollectionViewCell *)[self.facesCollectionView cellForItemAtIndexPath:pointIndexPath];
258 | [selectedCell highlightSelected:!selectedCell.highlightSelected];
259 |
260 | if ( selectedCell.highlightSelected ) {
261 | [self.dataSource addIndexPathToDelete:pointIndexPath];
262 | }
263 | else {
264 | [self.dataSource removeIndexPathToDelete:pointIndexPath];
265 | }
266 |
267 | const NSInteger itemsToDelete = [self.dataSource numberOfItemsToDelete];
268 | if ( itemsToDelete > 0 ) {
269 |
270 | if ( itemsToDelete > 1 ) {
271 | [self.deleteButton setTitle:kTHDeleteButtonMultiItemTitle forState:UIControlStateNormal];
272 | }
273 | else {
274 | [self.deleteButton setTitle:kTHDeleteButtonSingleItemTitle forState:UIControlStateNormal];
275 | }
276 |
277 | if ( self.deleteButton.isHidden ) {
278 | [self showDeleteButton:YES];
279 | }
280 | }
281 | else {
282 | if ( !self.deleteButton.isHidden ) {
283 | [self showDeleteButton:NO];
284 | }
285 | }
286 | }
287 | }
288 | break;
289 | }
290 | case UIGestureRecognizerStateEnded:
291 | case UIGestureRecognizerStateFailed:
292 | case UIGestureRecognizerStateCancelled:
293 | case UIGestureRecognizerStateChanged:
294 | default:
295 | break;
296 | }
297 | }
298 |
299 | #pragma mark - UIButton Selectors
300 |
301 | - (void)deleteSelectedItems
302 | {
303 | [self.dataSource deleteSelectedItems:^{
304 | [self showDeleteButton:NO];
305 | }];
306 | }
307 |
308 | @end
309 |
--------------------------------------------------------------------------------
/src/Views/Loading/MBProgressHUD.h:
--------------------------------------------------------------------------------
1 | //
2 | // MBProgressHUD.h
3 | // Version 0.9.1
4 | // Created by Matej Bukovinski on 2.4.09.
5 | //
6 |
7 | // This code is distributed under the terms and conditions of the MIT license.
8 |
9 | // Copyright (c) 2009-2015 Matej Bukovinski
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in
19 | // all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 | // THE SOFTWARE.
28 |
29 | #import
30 | #import
31 | #import
32 |
33 | @protocol MBProgressHUDDelegate;
34 |
35 |
36 | typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
37 | /** Progress is shown using an UIActivityIndicatorView. This is the default. */
38 | MBProgressHUDModeIndeterminate,
39 | /** Progress is shown using a round, pie-chart like, progress view. */
40 | MBProgressHUDModeDeterminate,
41 | /** Progress is shown using a horizontal progress bar */
42 | MBProgressHUDModeDeterminateHorizontalBar,
43 | /** Progress is shown using a ring-shaped progress view. */
44 | MBProgressHUDModeAnnularDeterminate,
45 | /** Shows a custom view */
46 | MBProgressHUDModeCustomView,
47 | /** Shows only labels */
48 | MBProgressHUDModeText
49 | };
50 |
51 | typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) {
52 | /** Opacity animation */
53 | MBProgressHUDAnimationFade,
54 | /** Opacity + scale animation */
55 | MBProgressHUDAnimationZoom,
56 | MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom,
57 | MBProgressHUDAnimationZoomIn
58 | };
59 |
60 |
61 | #ifndef MB_INSTANCETYPE
62 | #if __has_feature(objc_instancetype)
63 | #define MB_INSTANCETYPE instancetype
64 | #else
65 | #define MB_INSTANCETYPE id
66 | #endif
67 | #endif
68 |
69 | #ifndef MB_STRONG
70 | #if __has_feature(objc_arc)
71 | #define MB_STRONG strong
72 | #else
73 | #define MB_STRONG retain
74 | #endif
75 | #endif
76 |
77 | #ifndef MB_WEAK
78 | #if __has_feature(objc_arc_weak)
79 | #define MB_WEAK weak
80 | #elif __has_feature(objc_arc)
81 | #define MB_WEAK unsafe_unretained
82 | #else
83 | #define MB_WEAK assign
84 | #endif
85 | #endif
86 |
87 | #if NS_BLOCKS_AVAILABLE
88 | typedef void (^MBProgressHUDCompletionBlock)();
89 | #endif
90 |
91 |
92 | /**
93 | * Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
94 | *
95 | * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class.
96 | * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all
97 | * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is
98 | * drawn centered as a rounded semi-transparent view which resizes depending on the user specified content.
99 | *
100 | * This view supports four modes of operation:
101 | * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView
102 | * - MBProgressHUDModeDeterminate - shows a custom round progress indicator
103 | * - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator
104 | * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView)
105 | *
106 | * All three modes can have optional labels assigned:
107 | * - If the labelText property is set and non-empty then a label containing the provided content is placed below the
108 | * indicator view.
109 | * - If also the detailsLabelText property is set then another label is placed below the first label.
110 | */
111 | @interface MBProgressHUD : UIView
112 |
113 | /**
114 | * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:.
115 | *
116 | * @param view The view that the HUD will be added to
117 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
118 | * animations while appearing.
119 | * @return A reference to the created HUD.
120 | *
121 | * @see hideHUDForView:animated:
122 | * @see animationType
123 | */
124 | + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
125 |
126 | /**
127 | * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:.
128 | *
129 | * @param view The view that is going to be searched for a HUD subview.
130 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
131 | * animations while disappearing.
132 | * @return YES if a HUD was found and removed, NO otherwise.
133 | *
134 | * @see showHUDAddedTo:animated:
135 | * @see animationType
136 | */
137 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
138 |
139 | /**
140 | * Finds all the HUD subviews and hides them.
141 | *
142 | * @param view The view that is going to be searched for HUD subviews.
143 | * @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use
144 | * animations while disappearing.
145 | * @return the number of HUDs found and removed.
146 | *
147 | * @see hideHUDForView:animated:
148 | * @see animationType
149 | */
150 | + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated;
151 |
152 | /**
153 | * Finds the top-most HUD subview and returns it.
154 | *
155 | * @param view The view that is going to be searched.
156 | * @return A reference to the last HUD subview discovered.
157 | */
158 | + (MB_INSTANCETYPE)HUDForView:(UIView *)view;
159 |
160 | /**
161 | * Finds all HUD subviews and returns them.
162 | *
163 | * @param view The view that is going to be searched.
164 | * @return All found HUD views (array of MBProgressHUD objects).
165 | */
166 | + (NSArray *)allHUDsForView:(UIView *)view;
167 |
168 | /**
169 | * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with
170 | * window.bounds as the parameter.
171 | *
172 | * @param window The window instance that will provide the bounds for the HUD. Should be the same instance as
173 | * the HUD's superview (i.e., the window that the HUD will be added to).
174 | */
175 | - (id)initWithWindow:(UIWindow *)window;
176 |
177 | /**
178 | * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
179 | * view.bounds as the parameter
180 | *
181 | * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as
182 | * the HUD's superview (i.e., the view that the HUD will be added to).
183 | */
184 | - (id)initWithView:(UIView *)view;
185 |
186 | /**
187 | * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so
188 | * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread
189 | * (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest).
190 | *
191 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
192 | * animations while appearing.
193 | *
194 | * @see animationType
195 | */
196 | - (void)show:(BOOL)animated;
197 |
198 | /**
199 | * Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
200 | * hide the HUD when your task completes.
201 | *
202 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
203 | * animations while disappearing.
204 | *
205 | * @see animationType
206 | */
207 | - (void)hide:(BOOL)animated;
208 |
209 | /**
210 | * Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
211 | * hide the HUD when your task completes.
212 | *
213 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
214 | * animations while disappearing.
215 | * @param delay Delay in seconds until the HUD is hidden.
216 | *
217 | * @see animationType
218 | */
219 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay;
220 |
221 | /**
222 | * Shows the HUD while a background task is executing in a new thread, then hides the HUD.
223 | *
224 | * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
225 | * pool.
226 | *
227 | * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
228 | * @param target The object that the target method belongs to.
229 | * @param object An optional object to be passed to the method.
230 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
231 | * animations while (dis)appearing.
232 | */
233 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
234 |
235 | #if NS_BLOCKS_AVAILABLE
236 |
237 | /**
238 | * Shows the HUD while a block is executing on a background queue, then hides the HUD.
239 | *
240 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
241 | */
242 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block;
243 |
244 | /**
245 | * Shows the HUD while a block is executing on a background queue, then hides the HUD.
246 | *
247 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
248 | */
249 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion;
250 |
251 | /**
252 | * Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD.
253 | *
254 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
255 | */
256 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue;
257 |
258 | /**
259 | * Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD.
260 | *
261 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will
262 | * not use animations while (dis)appearing.
263 | * @param block The block to be executed while the HUD is shown.
264 | * @param queue The dispatch queue on which the block should be executed.
265 | * @param completion The block to be executed on completion.
266 | *
267 | * @see completionBlock
268 | */
269 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
270 | completionBlock:(MBProgressHUDCompletionBlock)completion;
271 |
272 | /**
273 | * A block that gets called after the HUD was completely hidden.
274 | */
275 | @property (copy) MBProgressHUDCompletionBlock completionBlock;
276 |
277 | #endif
278 |
279 | /**
280 | * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate.
281 | *
282 | * @see MBProgressHUDMode
283 | */
284 | @property (assign) MBProgressHUDMode mode;
285 |
286 | /**
287 | * The animation type that should be used when the HUD is shown and hidden.
288 | *
289 | * @see MBProgressHUDAnimation
290 | */
291 | @property (assign) MBProgressHUDAnimation animationType;
292 |
293 | /**
294 | * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
295 | * For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds).
296 | */
297 | @property (MB_STRONG) UIView *customView;
298 |
299 | /**
300 | * The HUD delegate object.
301 | *
302 | * @see MBProgressHUDDelegate
303 | */
304 | @property (MB_WEAK) id delegate;
305 |
306 | /**
307 | * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
308 | * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or
309 | * set to @"", then no message is displayed.
310 | */
311 | @property (copy) NSString *labelText;
312 |
313 | /**
314 | * An optional details message displayed below the labelText message. This message is displayed only if the labelText
315 | * property is also set and is different from an empty string (@""). The details text can span multiple lines.
316 | */
317 | @property (copy) NSString *detailsLabelText;
318 |
319 | /**
320 | * The opacity of the HUD window. Defaults to 0.8 (80% opacity).
321 | */
322 | @property (assign) float opacity;
323 |
324 | /**
325 | * The color of the HUD window. Defaults to black. If this property is set, color is set using
326 | * this UIColor and the opacity property is not used. using retain because performing copy on
327 | * UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone.
328 | */
329 | @property (MB_STRONG) UIColor *color;
330 |
331 | /**
332 | * The x-axis offset of the HUD relative to the centre of the superview.
333 | */
334 | @property (assign) float xOffset;
335 |
336 | /**
337 | * The y-axis offset of the HUD relative to the centre of the superview.
338 | */
339 | @property (assign) float yOffset;
340 |
341 | /**
342 | * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views).
343 | * Defaults to 20.0
344 | */
345 | @property (assign) float margin;
346 |
347 | /**
348 | * The corner radius for the HUD
349 | * Defaults to 10.0
350 | */
351 | @property (assign) float cornerRadius;
352 |
353 | /**
354 | * Cover the HUD background view with a radial gradient.
355 | */
356 | @property (assign) BOOL dimBackground;
357 |
358 | /*
359 | * Grace period is the time (in seconds) that the invoked method may be run without
360 | * showing the HUD. If the task finishes before the grace time runs out, the HUD will
361 | * not be shown at all.
362 | * This may be used to prevent HUD display for very short tasks.
363 | * Defaults to 0 (no grace time).
364 | * Grace time functionality is only supported when the task status is known!
365 | * @see taskInProgress
366 | */
367 | @property (assign) float graceTime;
368 |
369 | /**
370 | * The minimum time (in seconds) that the HUD is shown.
371 | * This avoids the problem of the HUD being shown and than instantly hidden.
372 | * Defaults to 0 (no minimum show time).
373 | */
374 | @property (assign) float minShowTime;
375 |
376 | /**
377 | * Indicates that the executed operation is in progress. Needed for correct graceTime operation.
378 | * If you don't set a graceTime (different than 0.0) this does nothing.
379 | * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:.
380 | * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly),
381 | * you need to set this property when your task starts and completes in order to have normal graceTime
382 | * functionality.
383 | */
384 | @property (assign) BOOL taskInProgress;
385 |
386 | /**
387 | * Removes the HUD from its parent view when hidden.
388 | * Defaults to NO.
389 | */
390 | @property (assign) BOOL removeFromSuperViewOnHide;
391 |
392 | /**
393 | * Font to be used for the main label. Set this property if the default is not adequate.
394 | */
395 | @property (MB_STRONG) UIFont* labelFont;
396 |
397 | /**
398 | * Color to be used for the main label. Set this property if the default is not adequate.
399 | */
400 | @property (MB_STRONG) UIColor* labelColor;
401 |
402 | /**
403 | * Font to be used for the details label. Set this property if the default is not adequate.
404 | */
405 | @property (MB_STRONG) UIFont* detailsLabelFont;
406 |
407 | /**
408 | * Color to be used for the details label. Set this property if the default is not adequate.
409 | */
410 | @property (MB_STRONG) UIColor* detailsLabelColor;
411 |
412 | /**
413 | * The color of the activity indicator. Defaults to [UIColor whiteColor]
414 | * Does nothing on pre iOS 5.
415 | */
416 | @property (MB_STRONG) UIColor *activityIndicatorColor;
417 |
418 | /**
419 | * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
420 | */
421 | @property (assign) float progress;
422 |
423 | /**
424 | * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size).
425 | */
426 | @property (assign) CGSize minSize;
427 |
428 |
429 | /**
430 | * The actual size of the HUD bezel.
431 | * You can use this to limit touch handling on the bezel aria only.
432 | * @see https://github.com/jdg/MBProgressHUD/pull/200
433 | */
434 | @property (atomic, assign, readonly) CGSize size;
435 |
436 |
437 | /**
438 | * Force the HUD dimensions to be equal if possible.
439 | */
440 | @property (assign, getter = isSquare) BOOL square;
441 |
442 | @end
443 |
444 |
445 | @protocol MBProgressHUDDelegate
446 |
447 | @optional
448 |
449 | /**
450 | * Called after the HUD was fully hidden from the screen.
451 | */
452 | - (void)hudWasHidden:(MBProgressHUD *)hud;
453 |
454 | @end
455 |
456 |
457 | /**
458 | * A progress view for showing definite progress by filling up a circle (pie chart).
459 | */
460 | @interface MBRoundProgressView : UIView
461 |
462 | /**
463 | * Progress (0.0 to 1.0)
464 | */
465 | @property (nonatomic, assign) float progress;
466 |
467 | /**
468 | * Indicator progress color.
469 | * Defaults to white [UIColor whiteColor]
470 | */
471 | @property (nonatomic, MB_STRONG) UIColor *progressTintColor;
472 |
473 | /**
474 | * Indicator background (non-progress) color.
475 | * Defaults to translucent white (alpha 0.1)
476 | */
477 | @property (nonatomic, MB_STRONG) UIColor *backgroundTintColor;
478 |
479 | /*
480 | * Display mode - NO = round or YES = annular. Defaults to round.
481 | */
482 | @property (nonatomic, assign, getter = isAnnular) BOOL annular;
483 |
484 | @end
485 |
486 |
487 | /**
488 | * A flat bar progress view.
489 | */
490 | @interface MBBarProgressView : UIView
491 |
492 | /**
493 | * Progress (0.0 to 1.0)
494 | */
495 | @property (nonatomic, assign) float progress;
496 |
497 | /**
498 | * Bar border line color.
499 | * Defaults to white [UIColor whiteColor].
500 | */
501 | @property (nonatomic, MB_STRONG) UIColor *lineColor;
502 |
503 | /**
504 | * Bar background color.
505 | * Defaults to clear [UIColor clearColor];
506 | */
507 | @property (nonatomic, MB_STRONG) UIColor *progressRemainingColor;
508 |
509 | /**
510 | * Bar progress color.
511 | * Defaults to white [UIColor whiteColor].
512 | */
513 | @property (nonatomic, MB_STRONG) UIColor *progressColor;
514 |
515 | @end
516 |
--------------------------------------------------------------------------------
/src/Views/Loading/MBProgressHUD.m:
--------------------------------------------------------------------------------
1 | //
2 | // MBProgressHUD.m
3 | // Version 0.9.1
4 | // Created by Matej Bukovinski on 2.4.09.
5 | //
6 |
7 | #import "MBProgressHUD.h"
8 | #import
9 |
10 |
11 | #if __has_feature(objc_arc)
12 | #define MB_AUTORELEASE(exp) exp
13 | #define MB_RELEASE(exp) exp
14 | #define MB_RETAIN(exp) exp
15 | #else
16 | #define MB_AUTORELEASE(exp) [exp autorelease]
17 | #define MB_RELEASE(exp) [exp release]
18 | #define MB_RETAIN(exp) [exp retain]
19 | #endif
20 |
21 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
22 | #define MBLabelAlignmentCenter NSTextAlignmentCenter
23 | #else
24 | #define MBLabelAlignmentCenter UITextAlignmentCenter
25 | #endif
26 |
27 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
28 | #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text \
29 | sizeWithAttributes:@{NSFontAttributeName:font}] : CGSizeZero;
30 | #else
31 | #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text sizeWithFont:font] : CGSizeZero;
32 | #endif
33 |
34 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
35 | #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
36 | boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin) \
37 | attributes:@{NSFontAttributeName:font} context:nil].size : CGSizeZero;
38 | #else
39 | #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
40 | sizeWithFont:font constrainedToSize:maxSize lineBreakMode:mode] : CGSizeZero;
41 | #endif
42 |
43 | #ifndef kCFCoreFoundationVersionNumber_iOS_7_0
44 | #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20
45 | #endif
46 |
47 | #ifndef kCFCoreFoundationVersionNumber_iOS_8_0
48 | #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
49 | #endif
50 |
51 |
52 | static const CGFloat kPadding = 4.f;
53 | static const CGFloat kLabelFontSize = 16.f;
54 | static const CGFloat kDetailsLabelFontSize = 12.f;
55 |
56 |
57 | @interface MBProgressHUD () {
58 | BOOL useAnimation;
59 | SEL methodForExecution;
60 | id targetForExecution;
61 | id objectForExecution;
62 | UILabel *label;
63 | UILabel *detailsLabel;
64 | BOOL isFinished;
65 | CGAffineTransform rotationTransform;
66 | }
67 |
68 | @property (atomic, MB_STRONG) UIView *indicator;
69 | @property (atomic, MB_STRONG) NSTimer *graceTimer;
70 | @property (atomic, MB_STRONG) NSTimer *minShowTimer;
71 | @property (atomic, MB_STRONG) NSDate *showStarted;
72 |
73 | @end
74 |
75 |
76 | @implementation MBProgressHUD
77 |
78 | #pragma mark - Properties
79 |
80 | @synthesize animationType;
81 | @synthesize delegate;
82 | @synthesize opacity;
83 | @synthesize color;
84 | @synthesize labelFont;
85 | @synthesize labelColor;
86 | @synthesize detailsLabelFont;
87 | @synthesize detailsLabelColor;
88 | @synthesize indicator;
89 | @synthesize xOffset;
90 | @synthesize yOffset;
91 | @synthesize minSize;
92 | @synthesize square;
93 | @synthesize margin;
94 | @synthesize dimBackground;
95 | @synthesize graceTime;
96 | @synthesize minShowTime;
97 | @synthesize graceTimer;
98 | @synthesize minShowTimer;
99 | @synthesize taskInProgress;
100 | @synthesize removeFromSuperViewOnHide;
101 | @synthesize customView;
102 | @synthesize showStarted;
103 | @synthesize mode;
104 | @synthesize labelText;
105 | @synthesize detailsLabelText;
106 | @synthesize progress;
107 | @synthesize size;
108 | @synthesize activityIndicatorColor;
109 | #if NS_BLOCKS_AVAILABLE
110 | @synthesize completionBlock;
111 | #endif
112 |
113 | #pragma mark - Class methods
114 |
115 | + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
116 | MBProgressHUD *hud = [[self alloc] initWithView:view];
117 | hud.removeFromSuperViewOnHide = YES;
118 | [view addSubview:hud];
119 | [hud show:animated];
120 | return MB_AUTORELEASE(hud);
121 | }
122 |
123 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
124 | MBProgressHUD *hud = [self HUDForView:view];
125 | if (hud != nil) {
126 | hud.removeFromSuperViewOnHide = YES;
127 | [hud hide:animated];
128 | return YES;
129 | }
130 | return NO;
131 | }
132 |
133 | + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
134 | NSArray *huds = [MBProgressHUD allHUDsForView:view];
135 | for (MBProgressHUD *hud in huds) {
136 | hud.removeFromSuperViewOnHide = YES;
137 | [hud hide:animated];
138 | }
139 | return [huds count];
140 | }
141 |
142 | + (MB_INSTANCETYPE)HUDForView:(UIView *)view {
143 | NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
144 | for (UIView *subview in subviewsEnum) {
145 | if ([subview isKindOfClass:self]) {
146 | return (MBProgressHUD *)subview;
147 | }
148 | }
149 | return nil;
150 | }
151 |
152 | + (NSArray *)allHUDsForView:(UIView *)view {
153 | NSMutableArray *huds = [NSMutableArray array];
154 | NSArray *subviews = view.subviews;
155 | for (UIView *aView in subviews) {
156 | if ([aView isKindOfClass:self]) {
157 | [huds addObject:aView];
158 | }
159 | }
160 | return [NSArray arrayWithArray:huds];
161 | }
162 |
163 | #pragma mark - Lifecycle
164 |
165 | - (id)initWithFrame:(CGRect)frame {
166 | self = [super initWithFrame:frame];
167 | if (self) {
168 | // Set default values for properties
169 | self.animationType = MBProgressHUDAnimationFade;
170 | self.mode = MBProgressHUDModeIndeterminate;
171 | self.labelText = nil;
172 | self.detailsLabelText = nil;
173 | self.opacity = 0.8f;
174 | self.color = nil;
175 | self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize];
176 | self.labelColor = [UIColor whiteColor];
177 | self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize];
178 | self.detailsLabelColor = [UIColor whiteColor];
179 | self.activityIndicatorColor = [UIColor whiteColor];
180 | self.xOffset = 0.0f;
181 | self.yOffset = 0.0f;
182 | self.dimBackground = NO;
183 | self.margin = 20.0f;
184 | self.cornerRadius = 10.0f;
185 | self.graceTime = 0.0f;
186 | self.minShowTime = 0.0f;
187 | self.removeFromSuperViewOnHide = NO;
188 | self.minSize = CGSizeZero;
189 | self.square = NO;
190 | self.contentMode = UIViewContentModeCenter;
191 | self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
192 | | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
193 |
194 | // Transparent background
195 | self.opaque = NO;
196 | self.backgroundColor = [UIColor clearColor];
197 | // Make it invisible for now
198 | self.alpha = 0.0f;
199 |
200 | taskInProgress = NO;
201 | rotationTransform = CGAffineTransformIdentity;
202 |
203 | [self setupLabels];
204 | [self updateIndicators];
205 | [self registerForKVO];
206 | [self registerForNotifications];
207 | }
208 | return self;
209 | }
210 |
211 | - (id)initWithView:(UIView *)view {
212 | NSAssert(view, @"View must not be nil.");
213 | return [self initWithFrame:view.bounds];
214 | }
215 |
216 | - (id)initWithWindow:(UIWindow *)window {
217 | return [self initWithView:window];
218 | }
219 |
220 | - (void)dealloc {
221 | [self unregisterFromNotifications];
222 | [self unregisterFromKVO];
223 | #if !__has_feature(objc_arc)
224 | [color release];
225 | [indicator release];
226 | [label release];
227 | [detailsLabel release];
228 | [labelText release];
229 | [detailsLabelText release];
230 | [graceTimer release];
231 | [minShowTimer release];
232 | [showStarted release];
233 | [customView release];
234 | [labelFont release];
235 | [labelColor release];
236 | [detailsLabelFont release];
237 | [detailsLabelColor release];
238 | #if NS_BLOCKS_AVAILABLE
239 | [completionBlock release];
240 | #endif
241 | [super dealloc];
242 | #endif
243 | }
244 |
245 | #pragma mark - Show & hide
246 |
247 | - (void)show:(BOOL)animated {
248 | useAnimation = animated;
249 | // If the grace time is set postpone the HUD display
250 | if (self.graceTime > 0.0) {
251 | self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self
252 | selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
253 | }
254 | // ... otherwise show the HUD imediately
255 | else {
256 | [self showUsingAnimation:useAnimation];
257 | }
258 | }
259 |
260 | - (void)hide:(BOOL)animated {
261 | useAnimation = animated;
262 | // If the minShow time is set, calculate how long the hud was shown,
263 | // and pospone the hiding operation if necessary
264 | if (self.minShowTime > 0.0 && showStarted) {
265 | NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
266 | if (interv < self.minShowTime) {
267 | self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self
268 | selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
269 | return;
270 | }
271 | }
272 | // ... otherwise hide the HUD immediately
273 | [self hideUsingAnimation:useAnimation];
274 | }
275 |
276 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
277 | [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay];
278 | }
279 |
280 | - (void)hideDelayed:(NSNumber *)animated {
281 | [self hide:[animated boolValue]];
282 | }
283 |
284 | #pragma mark - Timer callbacks
285 |
286 | - (void)handleGraceTimer:(NSTimer *)theTimer {
287 | // Show the HUD only if the task is still running
288 | if (taskInProgress) {
289 | [self showUsingAnimation:useAnimation];
290 | }
291 | }
292 |
293 | - (void)handleMinShowTimer:(NSTimer *)theTimer {
294 | [self hideUsingAnimation:useAnimation];
295 | }
296 |
297 | #pragma mark - View Hierrarchy
298 |
299 | - (void)didMoveToSuperview {
300 | [self updateForCurrentOrientationAnimated:NO];
301 | }
302 |
303 | #pragma mark - Internal show & hide operations
304 |
305 | - (void)showUsingAnimation:(BOOL)animated {
306 | // Cancel any scheduled hideDelayed: calls
307 | [NSObject cancelPreviousPerformRequestsWithTarget:self];
308 | [self setNeedsDisplay];
309 |
310 | if (animated && animationType == MBProgressHUDAnimationZoomIn) {
311 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
312 | } else if (animated && animationType == MBProgressHUDAnimationZoomOut) {
313 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
314 | }
315 | self.showStarted = [NSDate date];
316 | // Fade in
317 | if (animated) {
318 | [UIView beginAnimations:nil context:NULL];
319 | [UIView setAnimationDuration:0.30];
320 | self.alpha = 1.0f;
321 | if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) {
322 | self.transform = rotationTransform;
323 | }
324 | [UIView commitAnimations];
325 | }
326 | else {
327 | self.alpha = 1.0f;
328 | }
329 | }
330 |
331 | - (void)hideUsingAnimation:(BOOL)animated {
332 | // Fade out
333 | if (animated && showStarted) {
334 | [UIView beginAnimations:nil context:NULL];
335 | [UIView setAnimationDuration:0.30];
336 | [UIView setAnimationDelegate:self];
337 | [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
338 | // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
339 | // in the done method
340 | if (animationType == MBProgressHUDAnimationZoomIn) {
341 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
342 | } else if (animationType == MBProgressHUDAnimationZoomOut) {
343 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
344 | }
345 |
346 | self.alpha = 0.02f;
347 | [UIView commitAnimations];
348 | }
349 | else {
350 | self.alpha = 0.0f;
351 | [self done];
352 | }
353 | self.showStarted = nil;
354 | }
355 |
356 | - (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
357 | [self done];
358 | }
359 |
360 | - (void)done {
361 | [NSObject cancelPreviousPerformRequestsWithTarget:self];
362 | isFinished = YES;
363 | self.alpha = 0.0f;
364 | if (removeFromSuperViewOnHide) {
365 | [self removeFromSuperview];
366 | }
367 | #if NS_BLOCKS_AVAILABLE
368 | if (self.completionBlock) {
369 | self.completionBlock();
370 | self.completionBlock = NULL;
371 | }
372 | #endif
373 | if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
374 | [delegate performSelector:@selector(hudWasHidden:) withObject:self];
375 | }
376 | }
377 |
378 | #pragma mark - Threading
379 |
380 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
381 | methodForExecution = method;
382 | targetForExecution = MB_RETAIN(target);
383 | objectForExecution = MB_RETAIN(object);
384 | // Launch execution in new thread
385 | self.taskInProgress = YES;
386 | [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
387 | // Show HUD view
388 | [self show:animated];
389 | }
390 |
391 | #if NS_BLOCKS_AVAILABLE
392 |
393 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block {
394 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
395 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
396 | }
397 |
398 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion {
399 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
400 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion];
401 | }
402 |
403 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue {
404 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
405 | }
406 |
407 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
408 | completionBlock:(MBProgressHUDCompletionBlock)completion {
409 | self.taskInProgress = YES;
410 | self.completionBlock = completion;
411 | dispatch_async(queue, ^(void) {
412 | block();
413 | dispatch_async(dispatch_get_main_queue(), ^(void) {
414 | [self cleanUp];
415 | });
416 | });
417 | [self show:animated];
418 | }
419 |
420 | #endif
421 |
422 | - (void)launchExecution {
423 | @autoreleasepool {
424 | #pragma clang diagnostic push
425 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
426 | // Start executing the requested task
427 | [targetForExecution performSelector:methodForExecution withObject:objectForExecution];
428 | #pragma clang diagnostic pop
429 | // Task completed, update view in main thread (note: view operations should
430 | // be done only in the main thread)
431 | [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
432 | }
433 | }
434 |
435 | - (void)cleanUp {
436 | taskInProgress = NO;
437 | #if !__has_feature(objc_arc)
438 | [targetForExecution release];
439 | [objectForExecution release];
440 | #else
441 | targetForExecution = nil;
442 | objectForExecution = nil;
443 | #endif
444 | [self hide:useAnimation];
445 | }
446 |
447 | #pragma mark - UI
448 |
449 | - (void)setupLabels {
450 | label = [[UILabel alloc] initWithFrame:self.bounds];
451 | label.adjustsFontSizeToFitWidth = NO;
452 | label.textAlignment = NSTextAlignmentCenter;
453 | label.opaque = NO;
454 | label.backgroundColor = [UIColor clearColor];
455 | label.textColor = self.labelColor;
456 | label.font = self.labelFont;
457 | label.text = self.labelText;
458 | [self addSubview:label];
459 |
460 | detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
461 | detailsLabel.font = self.detailsLabelFont;
462 | detailsLabel.adjustsFontSizeToFitWidth = NO;
463 | detailsLabel.textAlignment = NSTextAlignmentCenter;
464 | detailsLabel.opaque = NO;
465 | detailsLabel.backgroundColor = [UIColor clearColor];
466 | detailsLabel.textColor = self.detailsLabelColor;
467 | detailsLabel.numberOfLines = 0;
468 | detailsLabel.font = self.detailsLabelFont;
469 | detailsLabel.text = self.detailsLabelText;
470 | [self addSubview:detailsLabel];
471 | }
472 |
473 | - (void)updateIndicators {
474 |
475 | BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
476 | BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
477 |
478 | if (mode == MBProgressHUDModeIndeterminate) {
479 | if (!isActivityIndicator) {
480 | // Update to indeterminate indicator
481 | [indicator removeFromSuperview];
482 | self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc]
483 | initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
484 | [(UIActivityIndicatorView *)indicator startAnimating];
485 | [self addSubview:indicator];
486 | }
487 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000
488 | [(UIActivityIndicatorView *)indicator setColor:self.activityIndicatorColor];
489 | #endif
490 | }
491 | else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
492 | // Update to bar determinate indicator
493 | [indicator removeFromSuperview];
494 | self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]);
495 | [self addSubview:indicator];
496 | }
497 | else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
498 | if (!isRoundIndicator) {
499 | // Update to determinante indicator
500 | [indicator removeFromSuperview];
501 | self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]);
502 | [self addSubview:indicator];
503 | }
504 | if (mode == MBProgressHUDModeAnnularDeterminate) {
505 | [(MBRoundProgressView *)indicator setAnnular:YES];
506 | }
507 | }
508 | else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
509 | // Update custom view indicator
510 | [indicator removeFromSuperview];
511 | self.indicator = customView;
512 | [self addSubview:indicator];
513 | } else if (mode == MBProgressHUDModeText) {
514 | [indicator removeFromSuperview];
515 | self.indicator = nil;
516 | }
517 | }
518 |
519 | #pragma mark - Layout
520 |
521 | - (void)layoutSubviews {
522 | [super layoutSubviews];
523 |
524 | // Entirely cover the parent view
525 | UIView *parent = self.superview;
526 | if (parent) {
527 | self.frame = parent.bounds;
528 | }
529 | CGRect bounds = self.bounds;
530 |
531 | // Determine the total widt and height needed
532 | CGFloat maxWidth = bounds.size.width - 4 * margin;
533 | CGSize totalSize = CGSizeZero;
534 |
535 | CGRect indicatorF = indicator.bounds;
536 | indicatorF.size.width = MIN(indicatorF.size.width, maxWidth);
537 | totalSize.width = MAX(totalSize.width, indicatorF.size.width);
538 | totalSize.height += indicatorF.size.height;
539 |
540 | CGSize labelSize = MB_TEXTSIZE(label.text, label.font);
541 | labelSize.width = MIN(labelSize.width, maxWidth);
542 | totalSize.width = MAX(totalSize.width, labelSize.width);
543 | totalSize.height += labelSize.height;
544 | if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
545 | totalSize.height += kPadding;
546 | }
547 |
548 | CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin;
549 | CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
550 | CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode);
551 | totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
552 | totalSize.height += detailsLabelSize.height;
553 | if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
554 | totalSize.height += kPadding;
555 | }
556 |
557 | totalSize.width += 2 * margin;
558 | totalSize.height += 2 * margin;
559 |
560 | // Position elements
561 | CGFloat yPos = round(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset;
562 | CGFloat xPos = xOffset;
563 | indicatorF.origin.y = yPos;
564 | indicatorF.origin.x = round((bounds.size.width - indicatorF.size.width) / 2) + xPos;
565 | indicator.frame = indicatorF;
566 | yPos += indicatorF.size.height;
567 |
568 | if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
569 | yPos += kPadding;
570 | }
571 | CGRect labelF;
572 | labelF.origin.y = yPos;
573 | labelF.origin.x = round((bounds.size.width - labelSize.width) / 2) + xPos;
574 | labelF.size = labelSize;
575 | label.frame = labelF;
576 | yPos += labelF.size.height;
577 |
578 | if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
579 | yPos += kPadding;
580 | }
581 | CGRect detailsLabelF;
582 | detailsLabelF.origin.y = yPos;
583 | detailsLabelF.origin.x = round((bounds.size.width - detailsLabelSize.width) / 2) + xPos;
584 | detailsLabelF.size = detailsLabelSize;
585 | detailsLabel.frame = detailsLabelF;
586 |
587 | // Enforce minsize and quare rules
588 | if (square) {
589 | CGFloat max = MAX(totalSize.width, totalSize.height);
590 | if (max <= bounds.size.width - 2 * margin) {
591 | totalSize.width = max;
592 | }
593 | if (max <= bounds.size.height - 2 * margin) {
594 | totalSize.height = max;
595 | }
596 | }
597 | if (totalSize.width < minSize.width) {
598 | totalSize.width = minSize.width;
599 | }
600 | if (totalSize.height < minSize.height) {
601 | totalSize.height = minSize.height;
602 | }
603 |
604 | size = totalSize;
605 | }
606 |
607 | #pragma mark BG Drawing
608 |
609 | - (void)drawRect:(CGRect)rect {
610 |
611 | CGContextRef context = UIGraphicsGetCurrentContext();
612 | UIGraphicsPushContext(context);
613 |
614 | if (self.dimBackground) {
615 | //Gradient colours
616 | size_t gradLocationsNum = 2;
617 | CGFloat gradLocations[2] = {0.0f, 1.0f};
618 | CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
619 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
620 | CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
621 | CGColorSpaceRelease(colorSpace);
622 | //Gradient center
623 | CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
624 | //Gradient radius
625 | float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
626 | //Gradient draw
627 | CGContextDrawRadialGradient (context, gradient, gradCenter,
628 | 0, gradCenter, gradRadius,
629 | kCGGradientDrawsAfterEndLocation);
630 | CGGradientRelease(gradient);
631 | }
632 |
633 | // Set background rect color
634 | if (self.color) {
635 | CGContextSetFillColorWithColor(context, self.color.CGColor);
636 | } else {
637 | CGContextSetGrayFillColor(context, 0.0f, self.opacity);
638 | }
639 |
640 |
641 | // Center HUD
642 | CGRect allRect = self.bounds;
643 | // Draw rounded HUD backgroud rect
644 | CGRect boxRect = CGRectMake(round((allRect.size.width - size.width) / 2) + self.xOffset,
645 | round((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
646 | float radius = self.cornerRadius;
647 | CGContextBeginPath(context);
648 | CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
649 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
650 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
651 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
652 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
653 | CGContextClosePath(context);
654 | CGContextFillPath(context);
655 |
656 | UIGraphicsPopContext();
657 | }
658 |
659 | #pragma mark - KVO
660 |
661 | - (void)registerForKVO {
662 | for (NSString *keyPath in [self observableKeypaths]) {
663 | [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
664 | }
665 | }
666 |
667 | - (void)unregisterFromKVO {
668 | for (NSString *keyPath in [self observableKeypaths]) {
669 | [self removeObserver:self forKeyPath:keyPath];
670 | }
671 | }
672 |
673 | - (NSArray *)observableKeypaths {
674 | return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", @"labelColor",
675 | @"detailsLabelText", @"detailsLabelFont", @"detailsLabelColor", @"progress", @"activityIndicatorColor", nil];
676 | }
677 |
678 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
679 | if (![NSThread isMainThread]) {
680 | [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
681 | } else {
682 | [self updateUIForKeypath:keyPath];
683 | }
684 | }
685 |
686 | - (void)updateUIForKeypath:(NSString *)keyPath {
687 | if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"] ||
688 | [keyPath isEqualToString:@"activityIndicatorColor"]) {
689 | [self updateIndicators];
690 | } else if ([keyPath isEqualToString:@"labelText"]) {
691 | label.text = self.labelText;
692 | } else if ([keyPath isEqualToString:@"labelFont"]) {
693 | label.font = self.labelFont;
694 | } else if ([keyPath isEqualToString:@"labelColor"]) {
695 | label.textColor = self.labelColor;
696 | } else if ([keyPath isEqualToString:@"detailsLabelText"]) {
697 | detailsLabel.text = self.detailsLabelText;
698 | } else if ([keyPath isEqualToString:@"detailsLabelFont"]) {
699 | detailsLabel.font = self.detailsLabelFont;
700 | } else if ([keyPath isEqualToString:@"detailsLabelColor"]) {
701 | detailsLabel.textColor = self.detailsLabelColor;
702 | } else if ([keyPath isEqualToString:@"progress"]) {
703 | if ([indicator respondsToSelector:@selector(setProgress:)]) {
704 | [(id)indicator setValue:@(progress) forKey:@"progress"];
705 | }
706 | return;
707 | }
708 | [self setNeedsLayout];
709 | [self setNeedsDisplay];
710 | }
711 |
712 | #pragma mark - Notifications
713 |
714 | - (void)registerForNotifications {
715 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
716 |
717 | [nc addObserver:self selector:@selector(statusBarOrientationDidChange:)
718 | name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
719 | }
720 |
721 | - (void)unregisterFromNotifications {
722 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
723 | [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
724 | }
725 |
726 | - (void)statusBarOrientationDidChange:(NSNotification *)notification {
727 | UIView *superview = self.superview;
728 | if (!superview) {
729 | return;
730 | } else {
731 | [self updateForCurrentOrientationAnimated:YES];
732 | }
733 | }
734 |
735 | - (void)updateForCurrentOrientationAnimated:(BOOL)animated {
736 | // Stay in sync with the superview in any case
737 | if (self.superview) {
738 | self.bounds = self.superview.bounds;
739 | [self setNeedsDisplay];
740 | }
741 |
742 | // Not needed on iOS 8+, compile out when the deployment target allows,
743 | // to avoid sharedApplication problems on extension targets
744 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000
745 | // Only needed pre iOS 7 when added to a window
746 | BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0;
747 | if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return;
748 |
749 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
750 | CGFloat radians = 0;
751 | if (UIInterfaceOrientationIsLandscape(orientation)) {
752 | if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; }
753 | else { radians = (CGFloat)M_PI_2; }
754 | // Window coordinates differ!
755 | self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
756 | } else {
757 | if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; }
758 | else { radians = 0; }
759 | }
760 | rotationTransform = CGAffineTransformMakeRotation(radians);
761 |
762 | if (animated) {
763 | [UIView beginAnimations:nil context:nil];
764 | [UIView setAnimationDuration:0.3];
765 | }
766 | [self setTransform:rotationTransform];
767 | if (animated) {
768 | [UIView commitAnimations];
769 | }
770 | #endif
771 | }
772 |
773 | @end
774 |
775 |
776 | @implementation MBRoundProgressView
777 |
778 | #pragma mark - Lifecycle
779 |
780 | - (id)init {
781 | return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
782 | }
783 |
784 | - (id)initWithFrame:(CGRect)frame {
785 | self = [super initWithFrame:frame];
786 | if (self) {
787 | self.backgroundColor = [UIColor clearColor];
788 | self.opaque = NO;
789 | _progress = 0.f;
790 | _annular = NO;
791 | _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
792 | _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
793 | [self registerForKVO];
794 | }
795 | return self;
796 | }
797 |
798 | - (void)dealloc {
799 | [self unregisterFromKVO];
800 | #if !__has_feature(objc_arc)
801 | [_progressTintColor release];
802 | [_backgroundTintColor release];
803 | [super dealloc];
804 | #endif
805 | }
806 |
807 | #pragma mark - Drawing
808 |
809 | - (void)drawRect:(CGRect)rect {
810 |
811 | CGRect allRect = self.bounds;
812 | CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
813 | CGContextRef context = UIGraphicsGetCurrentContext();
814 |
815 | if (_annular) {
816 | // Draw background
817 | BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
818 | CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f;
819 | UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
820 | processBackgroundPath.lineWidth = lineWidth;
821 | processBackgroundPath.lineCapStyle = kCGLineCapButt;
822 | CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
823 | CGFloat radius = (self.bounds.size.width - lineWidth)/2;
824 | CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
825 | CGFloat endAngle = (2 * (float)M_PI) + startAngle;
826 | [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
827 | [_backgroundTintColor set];
828 | [processBackgroundPath stroke];
829 | // Draw progress
830 | UIBezierPath *processPath = [UIBezierPath bezierPath];
831 | processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare;
832 | processPath.lineWidth = lineWidth;
833 | endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
834 | [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
835 | [_progressTintColor set];
836 | [processPath stroke];
837 | } else {
838 | // Draw background
839 | [_progressTintColor setStroke];
840 | [_backgroundTintColor setFill];
841 | CGContextSetLineWidth(context, 2.0f);
842 | CGContextFillEllipseInRect(context, circleRect);
843 | CGContextStrokeEllipseInRect(context, circleRect);
844 | // Draw progress
845 | CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
846 | CGFloat radius = (allRect.size.width - 4) / 2;
847 | CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
848 | CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
849 | [_progressTintColor setFill];
850 | CGContextMoveToPoint(context, center.x, center.y);
851 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
852 | CGContextClosePath(context);
853 | CGContextFillPath(context);
854 | }
855 | }
856 |
857 | #pragma mark - KVO
858 |
859 | - (void)registerForKVO {
860 | for (NSString *keyPath in [self observableKeypaths]) {
861 | [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
862 | }
863 | }
864 |
865 | - (void)unregisterFromKVO {
866 | for (NSString *keyPath in [self observableKeypaths]) {
867 | [self removeObserver:self forKeyPath:keyPath];
868 | }
869 | }
870 |
871 | - (NSArray *)observableKeypaths {
872 | return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", @"progress", @"annular", nil];
873 | }
874 |
875 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
876 | [self setNeedsDisplay];
877 | }
878 |
879 | @end
880 |
881 |
882 | @implementation MBBarProgressView
883 |
884 | #pragma mark - Lifecycle
885 |
886 | - (id)init {
887 | return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
888 | }
889 |
890 | - (id)initWithFrame:(CGRect)frame {
891 | self = [super initWithFrame:frame];
892 | if (self) {
893 | _progress = 0.f;
894 | _lineColor = [UIColor whiteColor];
895 | _progressColor = [UIColor whiteColor];
896 | _progressRemainingColor = [UIColor clearColor];
897 | self.backgroundColor = [UIColor clearColor];
898 | self.opaque = NO;
899 | [self registerForKVO];
900 | }
901 | return self;
902 | }
903 |
904 | - (void)dealloc {
905 | [self unregisterFromKVO];
906 | #if !__has_feature(objc_arc)
907 | [_lineColor release];
908 | [_progressColor release];
909 | [_progressRemainingColor release];
910 | [super dealloc];
911 | #endif
912 | }
913 |
914 | #pragma mark - Drawing
915 |
916 | - (void)drawRect:(CGRect)rect {
917 | CGContextRef context = UIGraphicsGetCurrentContext();
918 |
919 | CGContextSetLineWidth(context, 2);
920 | CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
921 | CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
922 |
923 | // Draw background
924 | float radius = (rect.size.height / 2) - 2;
925 | CGContextMoveToPoint(context, 2, rect.size.height/2);
926 | CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
927 | CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
928 | CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
929 | CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
930 | CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
931 | CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
932 | CGContextFillPath(context);
933 |
934 | // Draw border
935 | CGContextMoveToPoint(context, 2, rect.size.height/2);
936 | CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
937 | CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
938 | CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
939 | CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
940 | CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
941 | CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
942 | CGContextStrokePath(context);
943 |
944 | CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
945 | radius = radius - 2;
946 | float amount = self.progress * rect.size.width;
947 |
948 | // Progress in the middle area
949 | if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
950 | CGContextMoveToPoint(context, 4, rect.size.height/2);
951 | CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
952 | CGContextAddLineToPoint(context, amount, 4);
953 | CGContextAddLineToPoint(context, amount, radius + 4);
954 |
955 | CGContextMoveToPoint(context, 4, rect.size.height/2);
956 | CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
957 | CGContextAddLineToPoint(context, amount, rect.size.height - 4);
958 | CGContextAddLineToPoint(context, amount, radius + 4);
959 |
960 | CGContextFillPath(context);
961 | }
962 |
963 | // Progress in the right arc
964 | else if (amount > radius + 4) {
965 | float x = amount - (rect.size.width - radius - 4);
966 |
967 | CGContextMoveToPoint(context, 4, rect.size.height/2);
968 | CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
969 | CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
970 | float angle = -acos(x/radius);
971 | if (isnan(angle)) angle = 0;
972 | CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
973 | CGContextAddLineToPoint(context, amount, rect.size.height/2);
974 |
975 | CGContextMoveToPoint(context, 4, rect.size.height/2);
976 | CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
977 | CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
978 | angle = acos(x/radius);
979 | if (isnan(angle)) angle = 0;
980 | CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
981 | CGContextAddLineToPoint(context, amount, rect.size.height/2);
982 |
983 | CGContextFillPath(context);
984 | }
985 |
986 | // Progress is in the left arc
987 | else if (amount < radius + 4 && amount > 0) {
988 | CGContextMoveToPoint(context, 4, rect.size.height/2);
989 | CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
990 | CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
991 |
992 | CGContextMoveToPoint(context, 4, rect.size.height/2);
993 | CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
994 | CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
995 |
996 | CGContextFillPath(context);
997 | }
998 | }
999 |
1000 | #pragma mark - KVO
1001 |
1002 | - (void)registerForKVO {
1003 | for (NSString *keyPath in [self observableKeypaths]) {
1004 | [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
1005 | }
1006 | }
1007 |
1008 | - (void)unregisterFromKVO {
1009 | for (NSString *keyPath in [self observableKeypaths]) {
1010 | [self removeObserver:self forKeyPath:keyPath];
1011 | }
1012 | }
1013 |
1014 | - (NSArray *)observableKeypaths {
1015 | return [NSArray arrayWithObjects:@"lineColor", @"progressRemainingColor", @"progressColor", @"progress", nil];
1016 | }
1017 |
1018 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
1019 | [self setNeedsDisplay];
1020 | }
1021 |
1022 | @end
1023 |
--------------------------------------------------------------------------------
/src/Views/THFacePickerCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // THFacePickerCollectionViewCell.h
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/21/15.
6 | //
7 | //
8 |
9 | @interface THFacePickerCollectionViewCell : UICollectionViewCell
10 |
11 | @property (strong, nonatomic) NSIndexPath *currentIndexPath;
12 | @property (nonatomic, assign, readonly) BOOL highlightSelected;;
13 |
14 | - (void)startLoading;
15 | - (void)stopLoading;
16 | - (void)clearImage;
17 | - (void)highlightSelected:(BOOL)highlight;
18 | - (void)setFaceWithImage:(UIImage *)faceImage atIndexPath:(NSIndexPath *)indexPath;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/src/Views/THFacePickerCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // THFacePickerCollectionViewCell.m
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/21/15.
6 | //
7 | //
8 |
9 | #import "THFacePickerCollectionViewCell.h"
10 |
11 | @interface THFacePickerCollectionViewCell ()
12 |
13 | @property (nonatomic) UIImageView *faceImageView;
14 | @property (nonatomic) UIActivityIndicatorView *loadingIndicatorView;
15 | @property (nonatomic) UIView *selectedView;
16 |
17 | @property (nonatomic, assign, readwrite) BOOL highlightSelected;
18 |
19 | @end
20 |
21 | @implementation THFacePickerCollectionViewCell
22 |
23 | - (instancetype)initWithFrame:(CGRect)frame
24 | {
25 | self = [super initWithFrame:frame];
26 | if ( self ) {
27 | _faceImageView = [[UIImageView alloc] initWithFrame:frame];
28 | _faceImageView.contentMode = UIViewContentModeScaleAspectFill;
29 | _faceImageView.center = self.contentView.center;
30 | [self.contentView addSubview:_faceImageView];
31 |
32 | _loadingIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
33 | _loadingIndicatorView.center = self.contentView.center;
34 | [self.contentView addSubview:_loadingIndicatorView];
35 |
36 | _selectedView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height)];
37 | _selectedView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.8f];
38 | _selectedView.layer.borderColor = [UIColor whiteColor].CGColor;
39 | _selectedView.layer.borderWidth = 3.0f;
40 | _selectedView.alpha = 0.0f;
41 | [self.contentView addSubview:_selectedView];
42 |
43 | _highlightSelected = NO;
44 |
45 | self.clipsToBounds = YES;
46 | }
47 |
48 | return self;
49 | }
50 |
51 | #pragma mark - Public
52 |
53 | - (void)startLoading
54 | {
55 | [self.loadingIndicatorView startAnimating];
56 | self.loadingIndicatorView.hidden = NO;
57 | }
58 |
59 | - (void)stopLoading
60 | {
61 | [self.loadingIndicatorView stopAnimating];
62 | self.loadingIndicatorView.hidden = YES;
63 | }
64 |
65 | - (void)clearImage
66 | {
67 | self.faceImageView.image = nil;
68 | }
69 |
70 | - (void)setFaceWithImage:(UIImage *)faceImage atIndexPath:(NSIndexPath *)indexPath
71 | {
72 | if ( [indexPath isEqual:self.currentIndexPath] ) {
73 | self.faceImageView.image = faceImage;
74 | }
75 | }
76 |
77 | - (void)highlightSelected:(BOOL)highlight
78 | {
79 | self.highlightSelected = highlight;
80 |
81 | CGFloat targetAlhpa;
82 | if ( highlight ) {
83 | targetAlhpa = 1.0f;
84 | }
85 | else {
86 | targetAlhpa = 0.0f;
87 | }
88 |
89 | [UIView animateWithDuration:0.1f animations:^{
90 | self.selectedView.alpha = targetAlhpa;
91 | }];
92 | }
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/src/Views/THFacesCollectionReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // THFacesCollectionReusableView.h
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/22/15.
6 | //
7 | //
8 |
9 | @interface THFacesCollectionReusableView : UICollectionReusableView
10 |
11 | @property (nonatomic, assign) NSString *title;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/Views/THFacesCollectionReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // THFacesCollectionReusableView.m
3 | // THVideoFaceSwapper
4 | //
5 | // Created by Clayton Rieck on 4/22/15.
6 | //
7 | //
8 |
9 | #import "THFacesCollectionReusableView.h"
10 |
11 | @interface THFacesCollectionReusableView ()
12 |
13 | @property (nonatomic) UILabel *titleLabel;
14 |
15 | @end
16 |
17 | @implementation THFacesCollectionReusableView
18 |
19 | - (instancetype)initWithFrame:(CGRect)frame
20 | {
21 | self = [super initWithFrame:frame];
22 | if ( self ) {
23 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height)];
24 | _titleLabel.font = [UIFont systemFontOfSize:20.0f];
25 | _titleLabel.numberOfLines = 1;
26 | [self addSubview:_titleLabel];
27 | }
28 | return self;
29 | }
30 |
31 | - (void)setTitle:(NSString *)title
32 | {
33 | _title = title;
34 | self.titleLabel.text = title;
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/src/main.mm:
--------------------------------------------------------------------------------
1 | #include "ofMain.h"
2 | #include "ofApp.h"
3 |
4 | int main(){
5 | ofiOSWindowSettings settings;
6 | settings.enableRetina = false; // enables retina resolution if the device supports it.
7 | settings.enableDepth = false; // enables depth buffer for 3d drawing.
8 | settings.enableAntiAliasing = false; // enables anti-aliasing which smooths out graphics on the screen.
9 | settings.numOfAntiAliasingSamples = 0; // number of samples used for anti-aliasing.
10 | settings.enableHardwareOrientation = false; // enables native view orientation.
11 | settings.enableHardwareOrientationAnimation = false; // enables native orientation changes to be animated.
12 | settings.glesVersion = OFXIOS_RENDERER_ES1; // type of renderer to use, ES1, ES2, ES3
13 | settings.windowMode = OF_FULLSCREEN;
14 | ofCreateWindow(settings);
15 |
16 | return ofRunApp(new ofApp);
17 | }
18 |
--------------------------------------------------------------------------------
/src/ofApp.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ofMain.h"
4 | #include "ofxiOS.h"
5 | #include "ofxiOSExtras.h"
6 | #include "ofxOpenCv.h"
7 | #include "ofxCv.h"
8 | #include "Clone.h"
9 | #include "ofxFaceTracker.h"
10 | #include "ofxFaceTrackerThreaded.h"
11 |
12 | #include "THPhotoPickerViewController.h"
13 |
14 | using namespace ofxCv;
15 | using namespace cv;
16 |
17 | class ofApp : public ofxiOSApp {
18 |
19 | public:
20 | void setup();
21 | void update();
22 | void draw();
23 | void exit();
24 |
25 | void touchDown(ofTouchEventArgs & touch);
26 | void touchMoved(ofTouchEventArgs & touch);
27 | void touchUp(ofTouchEventArgs & touch);
28 | void touchDoubleTap(ofTouchEventArgs & touch);
29 | void touchCancelled(ofTouchEventArgs & touch);
30 |
31 | void lostFocus();
32 | void gotFocus();
33 | void gotMemoryWarning();
34 | void deviceOrientationChanged(int newOrientation);
35 |
36 | void setupCam(int width, int height);
37 | void dragEvent(ofDragInfo dragInfo);
38 | void loadFace(string face);
39 | void loadOFImage(ofImage image);
40 |
41 | ofxFaceTrackerThreaded camTracker;
42 | ofVideoGrabber cam;
43 | ofxCvColorImage colorCv;
44 | ofxCvColorImage srcColorCv;
45 |
46 | ofxFaceTracker srcTracker;
47 | ofImage src;
48 | vector srcPoints;
49 |
50 | bool cloneReady;
51 | Clone clone;
52 | ofFbo srcFbo, maskFbo;
53 |
54 | ofDirectory faces;
55 | };
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/ofApp.mm:
--------------------------------------------------------------------------------
1 | #include "ofApp.h"
2 |
3 | THPhotoPickerViewController *photoPicker;
4 |
5 | //--------------------------------------------------------------
6 | void ofApp::setup(){
7 | cout << ofxiOSGetUIWindow().bounds.size.width << " : " << ofxiOSGetUIWindow().bounds.size.height << endl;
8 | faces.allowExt("jpg");
9 | faces.allowExt("jpeg");
10 | faces.allowExt("png");
11 | faces.open("faces/");
12 | faces.listDir();
13 |
14 | ofSetVerticalSync(true);
15 | cloneReady = false;
16 |
17 | photoPicker = [[THPhotoPickerViewController alloc] init];
18 |
19 | int screenWidth = [UIScreen mainScreen].bounds.size.width;
20 | int screenHeight = [UIScreen mainScreen].bounds.size.height;
21 | setupCam(screenWidth, screenHeight);
22 |
23 | ofFbo::Settings settings;
24 | settings.width = cam.getWidth();
25 | settings.height = cam.getHeight();
26 | maskFbo.allocate(settings);
27 | srcFbo.allocate(settings);
28 | clone.setup(cam.getWidth(), cam.getHeight());
29 |
30 | camTracker.setup();
31 | srcTracker.setup();
32 | srcTracker.setIterations(15);
33 | srcTracker.setAttempts(4);
34 |
35 | if ( faces.size() > 0 ) {
36 | loadFace(faces.getPath(0));
37 | }
38 |
39 | colorCv.allocate(cam.getWidth(), cam.getHeight());
40 | }
41 |
42 | //--------------------------------------------------------------
43 | void ofApp::update(){
44 | cam.update();
45 | if(cam.isFrameNew()) {
46 | colorCv = cam.getPixels();
47 | camTracker.update(toCv(colorCv));
48 |
49 | cloneReady = camTracker.getFound();
50 | if(cloneReady) {
51 | ofMesh camMesh = camTracker.getImageMesh();
52 | camMesh.clearTexCoords();
53 | camMesh.addTexCoords(srcPoints);
54 |
55 | maskFbo.begin();
56 | ofClear(0, 255);
57 | camMesh.draw();
58 | maskFbo.end();
59 |
60 | srcFbo.begin();
61 | ofClear(0, 255);
62 | src.bind();
63 | camMesh.draw();
64 | src.unbind();
65 | srcFbo.end();
66 |
67 | clone.setStrength(16);
68 | clone.update(srcFbo.getTextureReference(), cam.getTextureReference(), maskFbo.getTextureReference());
69 | }
70 | }
71 | }
72 |
73 | //--------------------------------------------------------------
74 | void ofApp::draw(){
75 | ofSetColor(255);
76 | cam.draw(0, 0);
77 |
78 | if ( src.getWidth() == 0 ) {
79 | ofDrawBitmapStringHighlight("SELECT OR TAKE AN IMAGE", 10, 30);
80 | }
81 | else if( !srcTracker.getFound() ) {
82 | ofDrawBitmapStringHighlight("SELECTED IMAGE FACE CANNOT BE FOUND", 10, 30);
83 | }
84 | else if ( !camTracker.getFound() ) {
85 | ofDrawBitmapStringHighlight("CAMERA FACE NOT FOUND", 10, 30);
86 | }
87 | else {
88 | if( cloneReady ) {
89 | clone.draw(0, 0);
90 | ofMesh objectMesh = camTracker.getObjectMesh();
91 | for(int i=0; i< objectMesh.getTexCoords().size(); i++) {
92 | ofVec2f & texCoord = objectMesh.getTexCoords()[i];
93 | texCoord.x /= ofNextPow2(cam.getWidth());
94 | texCoord.y /= ofNextPow2(cam.getHeight());
95 | }
96 |
97 | ofMesh imgMesh = srcTracker.getObjectMesh();
98 | for(int i=0; i< imgMesh.getTexCoords().size(); i++) {
99 | ofVec2f & texCoord = imgMesh.getTexCoords()[i];
100 | texCoord.x /= ofNextPow2(src.getWidth());
101 | texCoord.y /= ofNextPow2(src.getHeight());
102 | }
103 |
104 | for(int i = 0; i < objectMesh.getNumVertices();i++){
105 | ofVec3f vertex = objectMesh.getVertex(i);
106 | imgMesh.setVertex(i, vertex);
107 | }
108 |
109 | ofVec2f position = camTracker.getPosition();
110 | float scale = camTracker.getScale();
111 | ofVec3f orientation = camTracker.getOrientation();
112 | ofPushMatrix();
113 | ofTranslate(position.x, position.y);
114 | ofScale(scale, scale, scale);
115 | ofRotateX(orientation.x * 45.0f);
116 | ofRotateY(orientation.y * 45.0f);
117 | ofRotateZ(orientation.z * 45.0f);
118 | ofSetColor(255,255,255,255);
119 | src.getTextureReference().bind();
120 | imgMesh.draw();
121 | src.getTextureReference().unbind();
122 | ofPopMatrix();
123 | src.draw(0, 0, 100, 100);
124 | }
125 | }
126 | }
127 |
128 | void ofApp::loadFace(string face){
129 | cloneReady = false;
130 | src.clear();
131 | src.loadImage(face);
132 |
133 | if(src.getWidth() > 0) {
134 | Mat cvImage = toCv(src);
135 | srcTracker.update(cvImage);
136 | srcPoints = srcTracker.getImagePoints();
137 | cloneReady = true;
138 | }
139 |
140 | }
141 |
142 | void ofApp::loadOFImage(ofImage input) {
143 |
144 | cloneReady = false;
145 | src.clear();
146 | srcPoints.clear();
147 | srcTracker.setup();
148 |
149 | if(input.getWidth() > 0) {
150 |
151 | if(input.getWidth() > input.getHeight()){
152 |
153 | input.resize(ofGetWidth(), input.getHeight()*ofGetWidth() /input.getWidth());
154 | }
155 | else{
156 |
157 | input.resize(input.getWidth()*ofGetHeight()/input.getHeight(), ofGetHeight());
158 | }
159 |
160 | src = input;
161 | Mat cvImage = toCv(input);
162 | srcTracker.update(cvImage);
163 | srcPoints = srcTracker.getImagePoints();
164 | cloneReady = true;
165 | }
166 | }
167 |
168 | void ofApp::setupCam(int width, int height) {
169 |
170 | cam.setDesiredFrameRate(24);
171 |
172 | if ( cam.listDevices().size() > 1 ) {
173 | cam.setDeviceID(1); // front facing camera
174 | }
175 | else {
176 | cam.setDeviceID(0); // rear facing camera
177 | }
178 |
179 | cam.initGrabber(width, height);
180 |
181 |
182 | if ( !camTracker.isThreadRunning() ) {
183 | camTracker.startThread();
184 | }
185 | }
186 |
187 | void ofApp::dragEvent(ofDragInfo dragInfo) {
188 |
189 | }
190 |
191 | //--------------------------------------------------------------
192 | void ofApp::exit(){
193 | camTracker.waitForThread();
194 | }
195 |
196 | //--------------------------------------------------------------
197 | void ofApp::touchDown(ofTouchEventArgs & touch){
198 | }
199 |
200 | //--------------------------------------------------------------
201 | void ofApp::touchMoved(ofTouchEventArgs & touch){
202 |
203 | }
204 |
205 | //--------------------------------------------------------------
206 | void ofApp::touchUp(ofTouchEventArgs & touch){
207 |
208 | }
209 |
210 | //--------------------------------------------------------------
211 | void ofApp::touchDoubleTap(ofTouchEventArgs & touch){
212 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:photoPicker];
213 | UIViewController *vc = (UIViewController *)ofxiOSGetViewController();
214 | [vc presentViewController:navController animated:YES completion:^{
215 | cam.close();
216 | camTracker.stopThread();
217 | }];
218 | }
219 |
220 | //--------------------------------------------------------------
221 | void ofApp::touchCancelled(ofTouchEventArgs & touch){
222 |
223 | }
224 |
225 | //--------------------------------------------------------------
226 | void ofApp::lostFocus(){
227 |
228 | }
229 |
230 | //--------------------------------------------------------------
231 | void ofApp::gotFocus(){
232 |
233 | }
234 |
235 | //--------------------------------------------------------------
236 | void ofApp::gotMemoryWarning(){
237 |
238 | }
239 |
240 | //--------------------------------------------------------------
241 | void ofApp::deviceOrientationChanged(int newOrientation){
242 |
243 | }
244 |
--------------------------------------------------------------------------------