├── 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 | ![A black and white dithered photograph of a sleeping cat taken from directly above the cat.](./misc/01_sleepy-cat.PNG) 9 | 10 | ## Screenshots of the app 11 | A screenshot of pico cam's primary UI. 12 | 13 | A screenshot of pico cam's info screen with some text. 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 | --------------------------------------------------------------------------------