├── misc
├── 02_the-app.PNG
├── 03_the-app.PNG
├── big-goblin.png
├── 01_sleepy-cat.PNG
└── goblin-purple-pants.png
├── pico cam
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── mac16.png
│ │ ├── mac32.png
│ │ ├── mac64.png
│ │ ├── ipad152.png
│ │ ├── ipad76.png
│ │ ├── mac1024.png
│ │ ├── mac128.png
│ │ ├── mac256.png
│ │ ├── mac512.png
│ │ ├── ipadPro167.png
│ │ ├── iphone120.png
│ │ ├── iphone180.png
│ │ ├── settings58.png
│ │ ├── settings87.png
│ │ ├── appstore1024.png
│ │ ├── spotlight120.png
│ │ ├── spotlight80.png
│ │ ├── ipadSettings29.png
│ │ ├── ipadSettings58.png
│ │ ├── ipadSpotlight40.png
│ │ ├── ipadSpotlight80.png
│ │ ├── notification40.png
│ │ ├── notification60.png
│ │ ├── ipadNotification20.png
│ │ ├── ipadNotification40.png
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── pico_camApp.swift
├── InfoView.swift
├── ContentView.swift
└── CameraView.swift
├── pico cam.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── eli.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── README.md
├── .gitignore
└── LICENSE
/misc/02_the-app.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/misc/02_the-app.PNG
--------------------------------------------------------------------------------
/misc/03_the-app.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/misc/03_the-app.PNG
--------------------------------------------------------------------------------
/misc/big-goblin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/misc/big-goblin.png
--------------------------------------------------------------------------------
/misc/01_sleepy-cat.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/misc/01_sleepy-cat.PNG
--------------------------------------------------------------------------------
/misc/goblin-purple-pants.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/misc/goblin-purple-pants.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/pico cam/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac16.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac32.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac64.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipad152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipad152.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipad76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipad76.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac1024.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac128.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac256.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/mac512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/mac512.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadPro167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadPro167.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/iphone120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/iphone120.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/iphone180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/iphone180.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/settings58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/settings58.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/settings87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/settings87.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/appstore1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/appstore1024.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/spotlight120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/spotlight120.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/spotlight80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/spotlight80.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/notification40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/notification40.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/notification60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/notification60.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eli-oat/pico-cam/HEAD/pico cam/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png
--------------------------------------------------------------------------------
/pico cam.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pico cam.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/pico cam/pico_camApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // pico_camApp.swift
3 | // pico cam
4 | //
5 | // Created by eli_oat on 8/10/24.
6 | //
7 |
8 | import SwiftUI
9 | import AVFoundation
10 |
11 | @main
12 | struct DitheredCameraApp: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | ContentView()
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pico cam.xcodeproj/xcuserdata/eli.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | pico cam.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pico Cam for iOS
2 |
3 | An iOS camera app for goblins.
4 |
5 | A re-implementation of the web version,
6 |
7 | ## Example output from the app
8 | 
9 |
10 | ## Screenshots of the app
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### macOS ###
2 | # General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | # Icon must end with two \r
8 | Icon
9 |
10 |
11 | # Thumbnails
12 | ._*
13 |
14 | # Files that might appear in the root of a volume
15 | .DocumentRevisions-V100
16 | .fseventsd
17 | .Spotlight-V100
18 | .TemporaryItems
19 | .Trashes
20 | .VolumeIcon.icns
21 | .com.apple.timemachine.donotpresent
22 |
23 | # Directories potentially created on remote AFP share
24 | .AppleDB
25 | .AppleDesktop
26 | Network Trash Folder
27 | Temporary Items
28 | .apdisk
29 |
30 | ### macOS Patch ###
31 | # iCloud generated files
32 | *.icloud
33 |
34 | ### Xcode ###
35 | ## User settings
36 | xcuserdata/
37 |
38 | ## Xcode 8 and earlier
39 | *.xcscmblueprint
40 | *.xccheckout
41 |
42 | ### Xcode Patch ###
43 | *.xcodeproj/*
44 | !*.xcodeproj/project.pbxproj
45 | !*.xcodeproj/xcshareddata/
46 | !*.xcodeproj/project.xcworkspace/
47 | !*.xcworkspace/contents.xcworkspacedata
48 | /*.gcno
49 | **/xcshareddata/WorkspaceSettings.xcsettings
50 |
51 | # End of https://www.toptal.com/developers/gitignore/api/macos,xcode
52 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 eli_oat
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/pico cam/InfoView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfoView.swift
3 | // pico cam
4 | //
5 | // Created by eli_oat on 8/10/24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct InfoView: View {
11 | @Environment(\.presentationMode) var presentationMode
12 |
13 | var body: some View {
14 | VStack {
15 | HStack {
16 | Spacer()
17 | Button(action: {
18 | // Dismiss the view by tapping "Done"
19 | presentationMode.wrappedValue.dismiss()
20 | }) {
21 | Text("Done")
22 | .font(.headline)
23 | .padding()
24 | }
25 | }
26 |
27 | Text("Pico Cam")
28 | .font(.largeTitle)
29 | .padding()
30 |
31 | Text("Pico Cam is a camera for goblins. For folks who remember the days of yore and a certain eye-ball shaped contraption that you could stick into a handheld game console.")
32 | .padding()
33 |
34 | Spacer()
35 | }
36 | .padding()
37 | }
38 | }
39 |
40 | struct InfoView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | InfoView()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/pico cam/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // pico cam
4 | //
5 | // Created by eli_oat on 8/10/24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | @State private var showInfo = false
12 | @State private var processedImage: UIImage?
13 | @State private var isSaving = false
14 | @State private var saveSuccess = false
15 |
16 | var body: some View {
17 | ZStack {
18 | // FIXME: this means that obscured behind the dithered preview there is a teeny tiny preview that shows unprocessed cameraa output -- is this really necessary?
19 | CameraView(processedImage: $processedImage)
20 | .scaleEffect(CGSize(width: 0.25, height: 0.25)) // make it smaller than the actual image preview so that it is hidden.
21 | .edgesIgnoringSafeArea(.all)
22 |
23 | // Preview dithered image
24 | if let processedImage = processedImage {
25 | Image(uiImage: processedImage)
26 | .resizable()
27 | .aspectRatio(contentMode: .fit)
28 | .edgesIgnoringSafeArea(.all)
29 | } else {
30 | // Placeholder while processing
31 | Color.black.edgesIgnoringSafeArea(.all)
32 | Text("No image available.")
33 | .foregroundColor(.white)
34 | }
35 |
36 | VStack {
37 | HStack {
38 | Button(action: {
39 | showInfo.toggle()
40 | }) {
41 | Circle()
42 | .fill(Color.white)
43 | .frame(width: 40, height: 40)
44 | .padding()
45 | .overlay(
46 | Image(systemName: "info.circle")
47 | .font(.largeTitle)
48 | .padding()
49 | .foregroundColor(.black) // Make sure that the button is visible
50 | )
51 | }
52 | Spacer()
53 | }
54 | Spacer()
55 | Button(action: {
56 | if let image = processedImage {
57 | saveImageToCameraRoll(image)
58 | }
59 | }) {
60 | Circle()
61 | .fill(Color.white)
62 | .frame(width: 70, height: 70)
63 | .overlay(
64 | Image(systemName: "camera.aperture")
65 | .font(.largeTitle)
66 | .foregroundColor(.black)
67 | )
68 | }
69 | .padding(.bottom, 30)
70 | }
71 | .zIndex(2) // FIXME: Where is the info button going!?
72 |
73 | if isSaving {
74 | VStack {
75 | Text("Saving...")
76 | .foregroundColor(.white)
77 | .padding()
78 | .background(Color.black.opacity(0.7))
79 | .cornerRadius(10)
80 | }
81 | .zIndex(2)
82 | }
83 |
84 | if saveSuccess {
85 | VStack {
86 | Text("Saved!")
87 | .foregroundColor(.white)
88 | .padding()
89 | .background(Color.black.opacity(0.7))
90 | .cornerRadius(10)
91 | }
92 | .onAppear {
93 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
94 | saveSuccess = false
95 | }
96 | }
97 | .zIndex(2)
98 | }
99 | }
100 | .sheet(isPresented: $showInfo) {
101 | InfoView()
102 | }
103 | }
104 |
105 | func saveImageToCameraRoll(_ image: UIImage) {
106 | isSaving = true
107 | UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
108 | isSaving = false
109 | saveSuccess = true
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/pico cam/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "notification40.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "notification60.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "settings58.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "settings87.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "spotlight80.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "spotlight120.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "iphone120.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "iphone180.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "filename" : "ipadNotification20.png",
53 | "idiom" : "ipad",
54 | "scale" : "1x",
55 | "size" : "20x20"
56 | },
57 | {
58 | "filename" : "ipadNotification40.png",
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "20x20"
62 | },
63 | {
64 | "filename" : "ipadSettings29.png",
65 | "idiom" : "ipad",
66 | "scale" : "1x",
67 | "size" : "29x29"
68 | },
69 | {
70 | "filename" : "ipadSettings58.png",
71 | "idiom" : "ipad",
72 | "scale" : "2x",
73 | "size" : "29x29"
74 | },
75 | {
76 | "filename" : "ipadSpotlight40.png",
77 | "idiom" : "ipad",
78 | "scale" : "1x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "filename" : "ipadSpotlight80.png",
83 | "idiom" : "ipad",
84 | "scale" : "2x",
85 | "size" : "40x40"
86 | },
87 | {
88 | "filename" : "ipad76.png",
89 | "idiom" : "ipad",
90 | "scale" : "1x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "filename" : "ipad152.png",
95 | "idiom" : "ipad",
96 | "scale" : "2x",
97 | "size" : "76x76"
98 | },
99 | {
100 | "filename" : "ipadPro167.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "83.5x83.5"
104 | },
105 | {
106 | "filename" : "appstore1024.png",
107 | "idiom" : "ios-marketing",
108 | "scale" : "1x",
109 | "size" : "1024x1024"
110 | },
111 | {
112 | "filename" : "mac16.png",
113 | "idiom" : "mac",
114 | "scale" : "1x",
115 | "size" : "16x16"
116 | },
117 | {
118 | "filename" : "mac32.png",
119 | "idiom" : "mac",
120 | "scale" : "2x",
121 | "size" : "16x16"
122 | },
123 | {
124 | "filename" : "mac32.png",
125 | "idiom" : "mac",
126 | "scale" : "1x",
127 | "size" : "32x32"
128 | },
129 | {
130 | "filename" : "mac64.png",
131 | "idiom" : "mac",
132 | "scale" : "2x",
133 | "size" : "32x32"
134 | },
135 | {
136 | "filename" : "mac128.png",
137 | "idiom" : "mac",
138 | "scale" : "1x",
139 | "size" : "128x128"
140 | },
141 | {
142 | "filename" : "mac256.png",
143 | "idiom" : "mac",
144 | "scale" : "2x",
145 | "size" : "128x128"
146 | },
147 | {
148 | "filename" : "mac256.png",
149 | "idiom" : "mac",
150 | "scale" : "1x",
151 | "size" : "256x256"
152 | },
153 | {
154 | "filename" : "mac512.png",
155 | "idiom" : "mac",
156 | "scale" : "2x",
157 | "size" : "256x256"
158 | },
159 | {
160 | "filename" : "mac512.png",
161 | "idiom" : "mac",
162 | "scale" : "1x",
163 | "size" : "512x512"
164 | },
165 | {
166 | "filename" : "mac1024.png",
167 | "idiom" : "mac",
168 | "scale" : "2x",
169 | "size" : "512x512"
170 | }
171 | ],
172 | "info" : {
173 | "author" : "xcode",
174 | "version" : 1
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/pico cam/CameraView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CameraView.swift
3 | // pico cam
4 | //
5 | // Created by eli_oat on 8/10/24.
6 | //
7 |
8 | import SwiftUI
9 | import AVFoundation
10 | import CoreImage
11 | import CoreImage.CIFilterBuiltins
12 |
13 | struct CameraView: UIViewControllerRepresentable {
14 | @Binding var processedImage: UIImage?
15 |
16 | class Coordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
17 | var parent: CameraView
18 |
19 | init(_ parent: CameraView) {
20 | self.parent = parent
21 | }
22 |
23 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
24 | print("captureOutput called")
25 | if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
26 | print("Image buffer captured.")
27 | let ciImage = CIImage(cvPixelBuffer: imageBuffer)
28 |
29 | // Apply the dithering function to the CIImage
30 | if let processedImage = self.parent.applyDithering(to: ciImage) {
31 | print("Dithered image successfully created.")
32 | DispatchQueue.main.async {
33 | self.parent.processedImage = processedImage
34 | }
35 | } else {
36 | print("Dithering failed, no processed image.")
37 | }
38 | } else {
39 | print("Failed to capture image buffer.")
40 | }
41 | }
42 | }
43 |
44 | func makeCoordinator() -> Coordinator {
45 | return Coordinator(self)
46 | }
47 |
48 | func makeUIViewController(context: Context) -> UIViewController {
49 | let controller = UIViewController()
50 |
51 | let captureSession = AVCaptureSession()
52 | captureSession.sessionPreset = .photo
53 | guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
54 | print("Failed to get the video device.")
55 | return controller
56 | }
57 |
58 | guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice), captureSession.canAddInput(videoDeviceInput) else {
59 | print("Failed to create video device input.")
60 | return controller
61 | }
62 | captureSession.addInput(videoDeviceInput)
63 |
64 | let videoOutput = AVCaptureVideoDataOutput()
65 | videoOutput.alwaysDiscardsLateVideoFrames = true // Handle dropped frames
66 | videoOutput.setSampleBufferDelegate(context.coordinator, queue: DispatchQueue(label: "videoQueue"))
67 | videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
68 |
69 | if captureSession.canAddOutput(videoOutput) {
70 | captureSession.addOutput(videoOutput)
71 | print("Video output added to session.")
72 | } else {
73 | print("Failed to add video output to session.")
74 | }
75 |
76 | let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
77 | previewLayer.videoGravity = .resizeAspectFill
78 |
79 | controller.view.layer.addSublayer(previewLayer)
80 | previewLayer.frame = controller.view.bounds
81 |
82 | // Capture session runs on a background thread
83 | DispatchQueue.global(qos: .userInitiated).async {
84 | captureSession.startRunning()
85 | print("Capture session started.")
86 | }
87 |
88 | return controller
89 | }
90 |
91 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
92 |
93 | func applyDithering(to ciImage: CIImage) -> UIImage? {
94 | print("Applying dithering...")
95 |
96 | // Gameboy camera is 160x144, but that makes math hard, so we go with dimensions closer to the phone
97 | // TODO: validate that this size works on things beside my phone...maybe?
98 | let targetSize = CGSize(width: 160, height: 120)
99 |
100 | let resizedCIImage = ciImage
101 | .transformed(by: CGAffineTransform(scaleX: targetSize.width / ciImage.extent.width, y: targetSize.height / ciImage.extent.height))
102 | .transformed(by: CGAffineTransform(rotationAngle: -.pi / 2)) // Rotate the image 90 degrees
103 |
104 | let context = CIContext()
105 | guard let cgImage = context.createCGImage(resizedCIImage, from: resizedCIImage.extent) else {
106 | print("Failed to create CGImage from CIImage.")
107 | return nil
108 | }
109 |
110 | let width = Int(resizedCIImage.extent.width)
111 | let height = Int(resizedCIImage.extent.height)
112 |
113 | guard let data = cgImage.dataProvider?.data,
114 | let ptr = CFDataGetBytePtr(data) else {
115 | print("Failed to get image data from CGImage.")
116 | return nil
117 | }
118 |
119 | var pixels = [UInt8](repeating: 0, count: width * height)
120 |
121 | // Convert to grayscale
122 | for y in 0.. 0 {
146 | pixels[(y + 1) * width + x - 1] = UInt8(clamping: Int(pixels[(y + 1) * width + x - 1]) + quantError * 3 / 16)
147 | }
148 | pixels[(y + 1) * width + x] = UInt8(clamping: Int(pixels[(y + 1) * width + x]) + quantError * 5 / 16)
149 | if x + 1 < width {
150 | pixels[(y + 1) * width + x + 1] = UInt8(clamping: Int(pixels[(y + 1) * width + x + 1]) + quantError * 1 / 16)
151 | }
152 | }
153 | }
154 | }
155 |
156 | // Make a new UIImage out of the dithered pixels
157 | let bitsPerComponent = 8
158 | let bitsPerPixel = 8
159 | let bytesPerRow = width
160 | let colorSpace = CGColorSpaceCreateDeviceGray()
161 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
162 |
163 | guard let providerRef = CGDataProvider(data: NSData(bytes: pixels, length: pixels.count)) else {
164 | print("Failed to create data provider for dithered image.")
165 | return nil
166 | }
167 |
168 | guard let ditheredCGImage = CGImage(width: width,
169 | height: height,
170 | bitsPerComponent: bitsPerComponent,
171 | bitsPerPixel: bitsPerPixel,
172 | bytesPerRow: bytesPerRow,
173 | space: colorSpace,
174 | bitmapInfo: bitmapInfo,
175 | provider: providerRef,
176 | decode: nil,
177 | shouldInterpolate: false,
178 | intent: .defaultIntent) else {
179 | print("Failed to create dithered CGImage.")
180 | return nil
181 | }
182 |
183 | print("Dithering completed successfully.")
184 |
185 | return UIImage(cgImage: ditheredCGImage)
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/pico cam.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4505D4A32C68F0930044F53D /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 4505D4A22C68F0930044F53D /* LICENSE */; };
11 | 45E751BD2C67D9B700EA4695 /* pico_camApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E751BC2C67D9B700EA4695 /* pico_camApp.swift */; };
12 | 45E751BF2C67D9B700EA4695 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E751BE2C67D9B700EA4695 /* ContentView.swift */; };
13 | 45E751C12C67D9B800EA4695 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 45E751C02C67D9B800EA4695 /* Assets.xcassets */; };
14 | 45E751C42C67D9B800EA4695 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 45E751C32C67D9B800EA4695 /* Preview Assets.xcassets */; };
15 | 45E751CB2C67DB0900EA4695 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E751CA2C67DB0900EA4695 /* CameraView.swift */; };
16 | 45E751CD2C67DBE300EA4695 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E751CC2C67DBE300EA4695 /* InfoView.swift */; };
17 | 45E751D12C68315900EA4695 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 45E751D02C68315900EA4695 /* README.md */; };
18 | 45E751D32C6831FC00EA4695 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 45E751D22C6831FC00EA4695 /* .gitignore */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 4505D4A22C68F0930044F53D /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
23 | 45E751B92C67D9B700EA4695 /* pico cam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "pico cam.app"; sourceTree = BUILT_PRODUCTS_DIR; };
24 | 45E751BC2C67D9B700EA4695 /* pico_camApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = pico_camApp.swift; sourceTree = ""; };
25 | 45E751BE2C67D9B700EA4695 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
26 | 45E751C02C67D9B800EA4695 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 45E751C32C67D9B800EA4695 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
28 | 45E751CA2C67DB0900EA4695 /* CameraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = ""; };
29 | 45E751CC2C67DBE300EA4695 /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = ""; };
30 | 45E751D02C68315900EA4695 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
31 | 45E751D22C6831FC00EA4695 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 45E751B62C67D9B700EA4695 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 45E751B02C67D9B700EA4695 = {
46 | isa = PBXGroup;
47 | children = (
48 | 4505D4A22C68F0930044F53D /* LICENSE */,
49 | 45E751D22C6831FC00EA4695 /* .gitignore */,
50 | 45E751D02C68315900EA4695 /* README.md */,
51 | 45E751BB2C67D9B700EA4695 /* pico cam */,
52 | 45E751BA2C67D9B700EA4695 /* Products */,
53 | );
54 | sourceTree = "";
55 | };
56 | 45E751BA2C67D9B700EA4695 /* Products */ = {
57 | isa = PBXGroup;
58 | children = (
59 | 45E751B92C67D9B700EA4695 /* pico cam.app */,
60 | );
61 | name = Products;
62 | sourceTree = "";
63 | };
64 | 45E751BB2C67D9B700EA4695 /* pico cam */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 45E751CC2C67DBE300EA4695 /* InfoView.swift */,
68 | 45E751BC2C67D9B700EA4695 /* pico_camApp.swift */,
69 | 45E751C02C67D9B800EA4695 /* Assets.xcassets */,
70 | 45E751BE2C67D9B700EA4695 /* ContentView.swift */,
71 | 45E751C22C67D9B800EA4695 /* Preview Content */,
72 | 45E751CA2C67DB0900EA4695 /* CameraView.swift */,
73 | );
74 | path = "pico cam";
75 | sourceTree = "";
76 | };
77 | 45E751C22C67D9B800EA4695 /* Preview Content */ = {
78 | isa = PBXGroup;
79 | children = (
80 | 45E751C32C67D9B800EA4695 /* Preview Assets.xcassets */,
81 | );
82 | path = "Preview Content";
83 | sourceTree = "";
84 | };
85 | /* End PBXGroup section */
86 |
87 | /* Begin PBXNativeTarget section */
88 | 45E751B82C67D9B700EA4695 /* pico cam */ = {
89 | isa = PBXNativeTarget;
90 | buildConfigurationList = 45E751C72C67D9B800EA4695 /* Build configuration list for PBXNativeTarget "pico cam" */;
91 | buildPhases = (
92 | 45E751B52C67D9B700EA4695 /* Sources */,
93 | 45E751B62C67D9B700EA4695 /* Frameworks */,
94 | 45E751B72C67D9B700EA4695 /* Resources */,
95 | );
96 | buildRules = (
97 | );
98 | dependencies = (
99 | );
100 | name = "pico cam";
101 | productName = "pico cam";
102 | productReference = 45E751B92C67D9B700EA4695 /* pico cam.app */;
103 | productType = "com.apple.product-type.application";
104 | };
105 | /* End PBXNativeTarget section */
106 |
107 | /* Begin PBXProject section */
108 | 45E751B12C67D9B700EA4695 /* Project object */ = {
109 | isa = PBXProject;
110 | attributes = {
111 | BuildIndependentTargetsInParallel = 1;
112 | LastSwiftUpdateCheck = 1540;
113 | LastUpgradeCheck = 1540;
114 | TargetAttributes = {
115 | 45E751B82C67D9B700EA4695 = {
116 | CreatedOnToolsVersion = 15.4;
117 | };
118 | };
119 | };
120 | buildConfigurationList = 45E751B42C67D9B700EA4695 /* Build configuration list for PBXProject "pico cam" */;
121 | compatibilityVersion = "Xcode 14.0";
122 | developmentRegion = en;
123 | hasScannedForEncodings = 0;
124 | knownRegions = (
125 | en,
126 | Base,
127 | );
128 | mainGroup = 45E751B02C67D9B700EA4695;
129 | productRefGroup = 45E751BA2C67D9B700EA4695 /* Products */;
130 | projectDirPath = "";
131 | projectRoot = "";
132 | targets = (
133 | 45E751B82C67D9B700EA4695 /* pico cam */,
134 | );
135 | };
136 | /* End PBXProject section */
137 |
138 | /* Begin PBXResourcesBuildPhase section */
139 | 45E751B72C67D9B700EA4695 /* Resources */ = {
140 | isa = PBXResourcesBuildPhase;
141 | buildActionMask = 2147483647;
142 | files = (
143 | 45E751D32C6831FC00EA4695 /* .gitignore in Resources */,
144 | 45E751D12C68315900EA4695 /* README.md in Resources */,
145 | 45E751C42C67D9B800EA4695 /* Preview Assets.xcassets in Resources */,
146 | 45E751C12C67D9B800EA4695 /* Assets.xcassets in Resources */,
147 | 4505D4A32C68F0930044F53D /* LICENSE in Resources */,
148 | );
149 | runOnlyForDeploymentPostprocessing = 0;
150 | };
151 | /* End PBXResourcesBuildPhase section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | 45E751B52C67D9B700EA4695 /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | 45E751CD2C67DBE300EA4695 /* InfoView.swift in Sources */,
159 | 45E751CB2C67DB0900EA4695 /* CameraView.swift in Sources */,
160 | 45E751BF2C67D9B700EA4695 /* ContentView.swift in Sources */,
161 | 45E751BD2C67D9B700EA4695 /* pico_camApp.swift in Sources */,
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXSourcesBuildPhase section */
166 |
167 | /* Begin XCBuildConfiguration section */
168 | 45E751C52C67D9B800EA4695 /* Debug */ = {
169 | isa = XCBuildConfiguration;
170 | buildSettings = {
171 | ALWAYS_SEARCH_USER_PATHS = NO;
172 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
173 | CLANG_ANALYZER_NONNULL = YES;
174 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
176 | CLANG_ENABLE_MODULES = YES;
177 | CLANG_ENABLE_OBJC_ARC = YES;
178 | CLANG_ENABLE_OBJC_WEAK = YES;
179 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
180 | CLANG_WARN_BOOL_CONVERSION = YES;
181 | CLANG_WARN_COMMA = YES;
182 | CLANG_WARN_CONSTANT_CONVERSION = YES;
183 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
184 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
185 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
186 | CLANG_WARN_EMPTY_BODY = YES;
187 | CLANG_WARN_ENUM_CONVERSION = YES;
188 | CLANG_WARN_INFINITE_RECURSION = YES;
189 | CLANG_WARN_INT_CONVERSION = YES;
190 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
191 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
192 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
194 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
195 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
196 | CLANG_WARN_STRICT_PROTOTYPES = YES;
197 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
198 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
206 | GCC_C_LANGUAGE_STANDARD = gnu17;
207 | GCC_DYNAMIC_NO_PIC = NO;
208 | GCC_NO_COMMON_BLOCKS = YES;
209 | GCC_OPTIMIZATION_LEVEL = 0;
210 | GCC_PREPROCESSOR_DEFINITIONS = (
211 | "DEBUG=1",
212 | "$(inherited)",
213 | );
214 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
215 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
216 | GCC_WARN_UNDECLARED_SELECTOR = YES;
217 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
218 | GCC_WARN_UNUSED_FUNCTION = YES;
219 | GCC_WARN_UNUSED_VARIABLE = YES;
220 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
221 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
222 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
223 | MTL_FAST_MATH = YES;
224 | ONLY_ACTIVE_ARCH = YES;
225 | SDKROOT = iphoneos;
226 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
227 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
228 | };
229 | name = Debug;
230 | };
231 | 45E751C62C67D9B800EA4695 /* Release */ = {
232 | isa = XCBuildConfiguration;
233 | buildSettings = {
234 | ALWAYS_SEARCH_USER_PATHS = NO;
235 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
236 | CLANG_ANALYZER_NONNULL = YES;
237 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
239 | CLANG_ENABLE_MODULES = YES;
240 | CLANG_ENABLE_OBJC_ARC = YES;
241 | CLANG_ENABLE_OBJC_WEAK = YES;
242 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
243 | CLANG_WARN_BOOL_CONVERSION = YES;
244 | CLANG_WARN_COMMA = YES;
245 | CLANG_WARN_CONSTANT_CONVERSION = YES;
246 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
247 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
248 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
249 | CLANG_WARN_EMPTY_BODY = YES;
250 | CLANG_WARN_ENUM_CONVERSION = YES;
251 | CLANG_WARN_INFINITE_RECURSION = YES;
252 | CLANG_WARN_INT_CONVERSION = YES;
253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
257 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
258 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
259 | CLANG_WARN_STRICT_PROTOTYPES = YES;
260 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
261 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
262 | CLANG_WARN_UNREACHABLE_CODE = YES;
263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
264 | COPY_PHASE_STRIP = NO;
265 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
266 | ENABLE_NS_ASSERTIONS = NO;
267 | ENABLE_STRICT_OBJC_MSGSEND = YES;
268 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
269 | GCC_C_LANGUAGE_STANDARD = gnu17;
270 | GCC_NO_COMMON_BLOCKS = YES;
271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
273 | GCC_WARN_UNDECLARED_SELECTOR = YES;
274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
275 | GCC_WARN_UNUSED_FUNCTION = YES;
276 | GCC_WARN_UNUSED_VARIABLE = YES;
277 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
278 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
279 | MTL_ENABLE_DEBUG_INFO = NO;
280 | MTL_FAST_MATH = YES;
281 | SDKROOT = iphoneos;
282 | SWIFT_COMPILATION_MODE = wholemodule;
283 | VALIDATE_PRODUCT = YES;
284 | };
285 | name = Release;
286 | };
287 | 45E751C82C67D9B800EA4695 /* Debug */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
292 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
293 | CODE_SIGN_STYLE = Automatic;
294 | CURRENT_PROJECT_VERSION = 1;
295 | DEVELOPMENT_ASSET_PATHS = "\"pico cam/Preview Content\"";
296 | DEVELOPMENT_TEAM = VQ3T773326;
297 | ENABLE_PREVIEWS = YES;
298 | GENERATE_INFOPLIST_FILE = YES;
299 | INFOPLIST_KEY_CFBundleDisplayName = "Pico Cam";
300 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography";
301 | INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "This application needs this permission so that it can take and save photos to your device.";
302 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
303 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
304 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
305 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
306 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
307 | LD_RUNPATH_SEARCH_PATHS = (
308 | "$(inherited)",
309 | "@executable_path/Frameworks",
310 | );
311 | MARKETING_VERSION = 1.0;
312 | PRODUCT_BUNDLE_IDENTIFIER = "com.eli.li.pico-cam";
313 | PRODUCT_NAME = "$(TARGET_NAME)";
314 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
315 | SUPPORTS_MACCATALYST = NO;
316 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
317 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
318 | SWIFT_EMIT_LOC_STRINGS = YES;
319 | SWIFT_VERSION = 5.0;
320 | TARGETED_DEVICE_FAMILY = 1;
321 | };
322 | name = Debug;
323 | };
324 | 45E751C92C67D9B800EA4695 /* Release */ = {
325 | isa = XCBuildConfiguration;
326 | buildSettings = {
327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
328 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
329 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
330 | CODE_SIGN_STYLE = Automatic;
331 | CURRENT_PROJECT_VERSION = 1;
332 | DEVELOPMENT_ASSET_PATHS = "\"pico cam/Preview Content\"";
333 | DEVELOPMENT_TEAM = VQ3T773326;
334 | ENABLE_PREVIEWS = YES;
335 | GENERATE_INFOPLIST_FILE = YES;
336 | INFOPLIST_KEY_CFBundleDisplayName = "Pico Cam";
337 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography";
338 | INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "This application needs this permission so that it can take and save photos to your device.";
339 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
340 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
341 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
342 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
343 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
344 | LD_RUNPATH_SEARCH_PATHS = (
345 | "$(inherited)",
346 | "@executable_path/Frameworks",
347 | );
348 | MARKETING_VERSION = 1.0;
349 | PRODUCT_BUNDLE_IDENTIFIER = "com.eli.li.pico-cam";
350 | PRODUCT_NAME = "$(TARGET_NAME)";
351 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
352 | SUPPORTS_MACCATALYST = NO;
353 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
354 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
355 | SWIFT_EMIT_LOC_STRINGS = YES;
356 | SWIFT_VERSION = 5.0;
357 | TARGETED_DEVICE_FAMILY = 1;
358 | };
359 | name = Release;
360 | };
361 | /* End XCBuildConfiguration section */
362 |
363 | /* Begin XCConfigurationList section */
364 | 45E751B42C67D9B700EA4695 /* Build configuration list for PBXProject "pico cam" */ = {
365 | isa = XCConfigurationList;
366 | buildConfigurations = (
367 | 45E751C52C67D9B800EA4695 /* Debug */,
368 | 45E751C62C67D9B800EA4695 /* Release */,
369 | );
370 | defaultConfigurationIsVisible = 0;
371 | defaultConfigurationName = Release;
372 | };
373 | 45E751C72C67D9B800EA4695 /* Build configuration list for PBXNativeTarget "pico cam" */ = {
374 | isa = XCConfigurationList;
375 | buildConfigurations = (
376 | 45E751C82C67D9B800EA4695 /* Debug */,
377 | 45E751C92C67D9B800EA4695 /* Release */,
378 | );
379 | defaultConfigurationIsVisible = 0;
380 | defaultConfigurationName = Release;
381 | };
382 | /* End XCConfigurationList section */
383 | };
384 | rootObject = 45E751B12C67D9B700EA4695 /* Project object */;
385 | }
386 |
--------------------------------------------------------------------------------