├── .gitignore ├── Cam Pack ├── CamPack.h ├── Camera Helper │ ├── CameraHelper.h │ └── CameraHelper.m ├── Orientation │ ├── EXIFGeometry.h │ ├── EXIFGeometry.m │ ├── ImageOrientation.h │ └── ImageOrientation.m └── UIImage+CoreImage │ ├── UIImage+CoreImage.h │ └── UIImage+CoreImage.m └── do-git /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/* 2 | !*.xcodeproj/project.pbxproj 3 | /build 4 | -------------------------------------------------------------------------------- /Cam Pack/CamPack.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | #import "CameraHelper.h" 8 | -------------------------------------------------------------------------------- /Cam Pack/Camera Helper/CameraHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | @import Foundation; 8 | @import UIKit; 9 | @import AVFoundation; 10 | @import CoreImage; 11 | @import CoreVideo; 12 | @import CoreMedia; 13 | @import QuartzCore; 14 | 15 | enum { 16 | kCameraNone = -1, 17 | kCameraBack, 18 | kCameraFront, 19 | } availableCameras; 20 | 21 | typedef enum { 22 | kAspect = 0, // AVLayerVideoGravityResizeAspect 23 | kResize, // AVLayerVideoGravityResize 24 | kFill // AVLayerVideoGravityResizeAspectFill 25 | } previewAspect; 26 | 27 | @protocol CameraHelperBarcodeDelegate 28 | - (void) processBarcode: (NSString *) barcode withType: (NSString *) codeType withMetadata: (AVMetadataObject *) metadata; 29 | @end 30 | 31 | // General Camera Assistance 32 | @interface CameraHelper : NSObject 33 | 34 | @property (nonatomic, readonly) AVCaptureSession *session; 35 | + (instancetype) newSession; 36 | - (void) startSession; 37 | - (void) stopSession; 38 | 39 | + (int) numberOfCameras; 40 | + (BOOL) backCameraAvailable; 41 | + (BOOL) frontCameraAvailable; 42 | 43 | // Get the names of these by sending .localizedName, e.g. 44 | // [CameraHelper frontCamera].localizedName 45 | + (AVCaptureDevice *)backCamera; 46 | + (AVCaptureDevice *)frontCamera; 47 | 48 | - (BOOL) isUsingFrontCamera; 49 | - (BOOL) isUsingBackCamera; 50 | - (void) useFrontCamera; 51 | - (void) useBackCamera; 52 | - (void) switchCameras; 53 | 54 | + (BOOL) supportsTorchMode; 55 | + (BOOL) isTorchModeOn; 56 | + (void) toggleTorchMode; 57 | 58 | + (AVCaptureVideoPreviewLayer *) previewInView: (UIView *) view; 59 | - (void) embedPreviewInView: (UIView *) aView; 60 | - (UIView *) previewWithFrame: (CGRect) aFrame; 61 | - (void) layoutPreviewInView: (UIView *) aView; 62 | 63 | // Use a higher video scale, e.g. 1.5 for barcode recognition 64 | - (void) setVideoOutputScale: (CGFloat) scaleFactor; 65 | - (void) removeOutputs; 66 | 67 | @property (nonatomic, readonly) CIImage *ciImage; 68 | @property (nonatomic, readonly) UIImage *currentImage; 69 | - (void) addImageGrabbingOutput; 70 | 71 | @property (nonatomic) id barcodeDelegate; 72 | - (void) addMetaDataOutput; 73 | 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Cam Pack/Camera Helper/CameraHelper.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | #import "CameraHelper.h" 8 | #import "ImageOrientation.h" 9 | #import "EXIFGeometry.h" 10 | #import "UIImage+CoreImage.h" 11 | 12 | // Camera Helper - General Camera Information 13 | 14 | @implementation CameraHelper 15 | #pragma mark - Instance 16 | 17 | - (instancetype) init 18 | { 19 | if (!(self = [super init])) return self; 20 | _session = [[AVCaptureSession alloc] init]; 21 | return self; 22 | } 23 | 24 | + (instancetype) newSession 25 | { 26 | return [[self alloc] init]; 27 | } 28 | 29 | #pragma mark - Info 30 | 31 | + (int) numberOfCameras 32 | { 33 | return [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo].count; 34 | } 35 | 36 | + (BOOL) backCameraAvailable 37 | { 38 | NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 39 | for (AVCaptureDevice *device in videoDevices) 40 | if (device.position == AVCaptureDevicePositionBack) return YES; 41 | return NO; 42 | } 43 | 44 | + (BOOL) frontCameraAvailable 45 | { 46 | NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 47 | for (AVCaptureDevice *device in videoDevices) 48 | if (device.position == AVCaptureDevicePositionFront) return YES; 49 | return NO; 50 | } 51 | 52 | + (AVCaptureDevice *)backCamera 53 | { 54 | NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 55 | for (AVCaptureDevice *device in videoDevices) 56 | if (device.position == AVCaptureDevicePositionBack) return device; 57 | 58 | return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 59 | } 60 | 61 | + (AVCaptureDevice *)frontCamera 62 | { 63 | NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 64 | for (AVCaptureDevice *device in videoDevices) 65 | if (device.position == AVCaptureDevicePositionFront) return device; 66 | 67 | return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 68 | } 69 | 70 | + (BOOL) supportsTorchMode 71 | { 72 | if (![self backCameraAvailable]) return NO; 73 | 74 | AVCaptureDevice *backCamera = [self backCamera]; 75 | if (!backCamera.hasTorch) return NO; 76 | 77 | return [backCamera isTorchModeSupported:AVCaptureTorchModeOn]; 78 | } 79 | 80 | + (BOOL) isTorchModeOn 81 | { 82 | if (![self supportsTorchMode]) return NO; 83 | 84 | AVCaptureDevice *backCamera = [self backCamera]; 85 | return (backCamera.torchMode == AVCaptureTorchModeOn); 86 | } 87 | 88 | + (void) toggleTorchMode 89 | { 90 | if (![self supportsTorchMode]) return; 91 | 92 | AVCaptureDevice *backCamera = [self backCamera]; 93 | NSError *error; 94 | if ([backCamera lockForConfiguration:&error]) 95 | { 96 | backCamera.torchMode = [self isTorchModeOn] ? AVCaptureTorchModeOff : AVCaptureTorchModeOn; 97 | [backCamera unlockForConfiguration]; 98 | } 99 | else 100 | NSLog(@"Could not configure back camera: %@", error.localizedDescription); 101 | 102 | } 103 | 104 | #pragma mark - Capture Inputs 105 | 106 | - (void) useDevice: (AVCaptureDevice *) newDevice 107 | { 108 | [_session beginConfiguration]; 109 | 110 | // Remove existing inputs 111 | NSArray *inputs = _session.inputs; 112 | for (AVCaptureInput *input in inputs) 113 | [_session removeInput:input]; 114 | 115 | AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:newDevice error:nil]; 116 | [_session addInput:captureInput]; 117 | 118 | [_session commitConfiguration]; 119 | } 120 | 121 | - (void) useFrontCamera 122 | { 123 | [self useDevice:[CameraHelper frontCamera]]; 124 | } 125 | 126 | - (void) useBackCamera 127 | { 128 | [self useDevice:[CameraHelper backCamera]]; 129 | } 130 | 131 | - (BOOL) isUsingFrontCamera 132 | { 133 | NSArray *inputs = _session.inputs; 134 | if (!inputs.count) return NO; 135 | 136 | for (AVCaptureDeviceInput *input in inputs) 137 | { 138 | if ([input.device.uniqueID isEqual:[CameraHelper frontCamera].uniqueID]) 139 | return YES; 140 | } 141 | return NO; 142 | } 143 | 144 | - (BOOL) isUsingBackCamera 145 | { 146 | NSArray *inputs = _session.inputs; 147 | if (!inputs.count) return NO; 148 | 149 | for (AVCaptureDeviceInput *input in inputs) 150 | { 151 | if ([input.device.uniqueID isEqual:[CameraHelper backCamera].uniqueID]) 152 | return YES; 153 | } 154 | return NO; 155 | 156 | } 157 | 158 | - (void) switchCameras 159 | { 160 | // Only switch if more than one camera available 161 | if (![CameraHelper numberOfCameras] > 1) return; 162 | 163 | // Only switch if a camera is in use 164 | if (!_session.inputs.count) return; 165 | 166 | // Perform switch 167 | AVCaptureDevice *newDevice = [self isUsingFrontCamera] ? [CameraHelper backCamera] : [CameraHelper frontCamera]; 168 | [self useDevice:newDevice]; 169 | } 170 | 171 | #pragma mark - Previews 172 | 173 | - (void) embedPreviewInView: (UIView *) aView 174 | { 175 | if (!_session) return; 176 | 177 | AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession: _session]; 178 | preview.frame = aView.bounds; 179 | // preview.videoGravity = AVLayerVideoGravityResizeAspect; // hmmm. 180 | preview.videoGravity = AVLayerVideoGravityResizeAspectFill; 181 | [aView.layer addSublayer: preview]; 182 | } 183 | 184 | - (UIView *) previewWithFrame: (CGRect) aFrame 185 | { 186 | if (!_session) return nil; 187 | 188 | UIView *view = [[UIView alloc] initWithFrame:aFrame]; 189 | [self embedPreviewInView:view]; 190 | 191 | return view; 192 | } 193 | 194 | + (AVCaptureVideoPreviewLayer *) previewInView: (UIView *) view 195 | { 196 | for (CALayer *layer in view.layer.sublayers) 197 | if ([layer isKindOfClass:[AVCaptureVideoPreviewLayer class]]) 198 | return (AVCaptureVideoPreviewLayer *)layer; 199 | 200 | return nil; 201 | } 202 | 203 | - (void) layoutPreviewInView: (UIView *) aView 204 | { 205 | AVCaptureVideoPreviewLayer *layer = [CameraHelper previewInView:aView]; 206 | if (!layer) return; 207 | 208 | UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; 209 | CATransform3D transform = CATransform3DIdentity; 210 | if (orientation == UIDeviceOrientationPortrait) ; 211 | else if (orientation == UIDeviceOrientationLandscapeLeft) 212 | transform = CATransform3DMakeRotation(-M_PI_2, 0.0f, 0.0f, 1.0f); 213 | else if (orientation == UIDeviceOrientationLandscapeRight) 214 | transform = CATransform3DMakeRotation(M_PI_2, 0.0f, 0.0f, 1.0f); 215 | else if (orientation == UIDeviceOrientationPortraitUpsideDown) 216 | transform = CATransform3DMakeRotation(M_PI, 0.0f, 0.0f, 1.0f); 217 | 218 | layer.transform = transform; 219 | layer.frame = aView.bounds; 220 | } 221 | 222 | #pragma mark - Output Cleanup 223 | 224 | - (void) setVideoOutputScale: (CGFloat) scaleFactor 225 | { 226 | [_session beginConfiguration]; 227 | 228 | NSArray *outputs = _session.outputs; 229 | for (AVCaptureOutput *output in outputs) 230 | for (AVCaptureConnection *connection in output.connections) 231 | connection.videoScaleAndCropFactor = scaleFactor; 232 | 233 | [_session commitConfiguration]; 234 | 235 | } 236 | 237 | - (void) removeOutputs 238 | { 239 | [_session beginConfiguration]; 240 | 241 | // Remove existing outputs 242 | NSArray *outputs = _session.outputs; 243 | for (AVCaptureOutput *output in outputs) 244 | [_session removeOutput:output]; 245 | 246 | [_session commitConfiguration]; 247 | } 248 | 249 | #pragma mark - Image Grabbing 250 | 251 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 252 | { 253 | @autoreleasepool 254 | { 255 | CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 256 | CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate); 257 | _ciImage = [[CIImage alloc] initWithCVPixelBuffer:imageBuffer options:(__bridge_transfer NSDictionary *)attachments]; 258 | } 259 | } 260 | 261 | - (void) addImageGrabbingOutput 262 | { 263 | [_session beginConfiguration]; 264 | 265 | // Remove existing outputs 266 | NSArray *outputs = _session.outputs; 267 | for (AVCaptureOutput *output in outputs) 268 | [_session removeOutput:output]; 269 | 270 | // Create capture output 271 | // Update thanks to Jake Marsh who points out not to use the main queue 272 | char *queueName = "com.sadun.tasks.grabFrames"; 273 | dispatch_queue_t queue = dispatch_queue_create(queueName, NULL); 274 | AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init]; 275 | captureOutput.alwaysDiscardsLateVideoFrames = YES; 276 | [captureOutput setSampleBufferDelegate:self queue:queue]; 277 | [captureOutput setVideoSettings:@{(NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)}]; 278 | 279 | [_session addOutput:captureOutput]; 280 | [_session commitConfiguration]; 281 | } 282 | 283 | - (UIImage *) currentImage 284 | { 285 | UIImageOrientation orientation = CurrentImageOrientation([self isUsingFrontCamera], NO); 286 | CIImage *ciimage = self.ciImage; 287 | UIImage *uiimage = [UIImage imageWithCIImage:ciimage orientation:orientation]; 288 | return uiimage; 289 | } 290 | 291 | #pragma mark - Metadata 292 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection 293 | { 294 | // NSLog(@"%@", [(AVCaptureMetadataOutput *) captureOutput availableMetadataObjectTypes]); 295 | 296 | // These are arbitrary. Pick and choose whichever types you like 297 | NSArray *codeTypes = @[AVMetadataObjectTypeUPCECode, 298 | AVMetadataObjectTypeCode39Code, 299 | AVMetadataObjectTypeCode39Mod43Code, 300 | AVMetadataObjectTypeEAN13Code, 301 | AVMetadataObjectTypeEAN8Code, 302 | AVMetadataObjectTypeCode93Code, 303 | AVMetadataObjectTypeCode128Code, 304 | AVMetadataObjectTypePDF417Code, 305 | AVMetadataObjectTypeQRCode, 306 | AVMetadataObjectTypeAztecCode]; 307 | 308 | for (AVMetadataObject *metadata in metadataObjects) 309 | { 310 | if ([codeTypes containsObject:metadata.type]) 311 | { 312 | // Match 313 | NSString *stringValue = [(AVMetadataMachineReadableCodeObject *)metadata stringValue]; 314 | [_barcodeDelegate processBarcode:stringValue withType:metadata.type withMetadata:metadata]; 315 | } 316 | else 317 | NSLog(@"Captured unknown metadata object: %@", metadata.type); 318 | 319 | } 320 | } 321 | 322 | - (void) addMetaDataOutput 323 | { 324 | [_session beginConfiguration]; 325 | 326 | // Remove existing outputs 327 | NSArray *outputs = _session.outputs; 328 | for (AVCaptureOutput *output in outputs) 329 | [_session removeOutput:output]; 330 | 331 | // Create capture output 332 | AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; 333 | [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; 334 | 335 | [_session addOutput:output]; 336 | output.metadataObjectTypes = output.availableMetadataObjectTypes; 337 | [_session commitConfiguration]; 338 | } 339 | 340 | #pragma mark - Start/Stop 341 | 342 | - (void) startSession 343 | { 344 | if (_session.running) return; 345 | [_session startRunning]; 346 | } 347 | 348 | - (void) stopSession 349 | { 350 | [_session stopRunning]; 351 | } 352 | @end -------------------------------------------------------------------------------- /Cam Pack/Orientation/EXIFGeometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | Convert CG points, sizes, and rects based on EXIF orientation 6 | 7 | */ 8 | 9 | #import 10 | #import "ImageOrientation.h" 11 | 12 | CGPoint PointInEXIF(ExifOrientation exifOrientation, CGPoint aPoint, CGRect rect); 13 | CGSize SizeInEXIF(ExifOrientation exifOrientation, CGSize aSize); 14 | CGRect RectInEXIF(ExifOrientation exifOrientation, CGRect inner, CGRect outer); -------------------------------------------------------------------------------- /Cam Pack/Orientation/EXIFGeometry.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | #import "EXIFGeometry.h" 8 | 9 | // Correspondences Updated 3/31/14. Thanks Andrea, who pointed out I'd swapped out 6 and 8. 10 | // I have not yet had time to update and test the geometric transformations 11 | 12 | /* 13 | enum { 14 | kTopLeft = 1, // UIImageOrientationUp, (0,0) at top left 15 | kTopRight = 2, // UIImageOrientationUpMirrored, (0,0) at top right 16 | kBottomRight = 3, // UIImageOrientationDown (0,0) at bottom right 17 | kBottomLeft = 4, // UIImageOrientationDownMirrored (0,0) at bottom left 18 | kLeftTop = 5, // UIImageOrientationLeftMirrored (0,0) at left top 19 | kLeftBottom = 6, // UIImageOrientationLeft (0,0) at right top 20 | kRightBottom = 7, // UIImageOrientationRightMirrored (0,0) at right bottom 21 | kRightTop = 8 // UIImageOrientationRight (0,0) at left bottom 22 | } ExifOrientation; 23 | 24 | topleft toprt botrt botleft leftop leftbot rightbot righttop 25 | EXIF 1 2 3 4 5 6 7 8 26 | 27 | XXXXXX XXXXXX XX XX XXXXXXXXXX XX XX XXXXXXXXXX 28 | XX XX XX XX XX XX XX XX XX XX XX XX 29 | XXXX XXXX XXXX XXXX XX XXXXXXXXXX XXXXXXXXXX XX 30 | XX XX XX XX 31 | XX XX XXXXXX XXXXXX 32 | 33 | UI 0 4 1 5 6 3 7 2 34 | up upmirror down downmir leftmir right rightmir left 35 | 36 | */ 37 | 38 | CGSize SizeInEXIF(ExifOrientation exifOrientation, CGSize aSize) 39 | { 40 | switch(exifOrientation) 41 | { 42 | case kTopLeft: 43 | case kTopRight: 44 | case kBottomRight: 45 | case kBottomLeft: 46 | return aSize; 47 | 48 | case kLeftTop: 49 | case kRightTop: 50 | case kRightBottom: 51 | case kLeftBottom: 52 | return CGSizeMake(aSize.height, aSize.width); 53 | } 54 | } 55 | 56 | CGPoint PointInEXIF(ExifOrientation exifOrientation, CGPoint aPoint, CGRect rect) 57 | { 58 | switch(exifOrientation) 59 | { 60 | case kTopLeft: // vetted - back -- NOT FOR FRONT 61 | return CGPointMake(aPoint.x, rect.size.height - aPoint.y); 62 | case kBottomLeft: 63 | return CGPointMake(aPoint.x, aPoint.y); 64 | 65 | case kTopRight: 66 | return CGPointMake(rect.size.width - aPoint.x, rect.size.height - aPoint.y); 67 | case kBottomRight: // vetted - back 68 | return CGPointMake(rect.size.width - aPoint.x, aPoint.y); 69 | 70 | case kLeftTop: // vetted - only for back -- NOT FOR FRONT 71 | return CGPointMake(aPoint.y, aPoint.x); 72 | case kLeftBottom: // untested 73 | return CGPointMake(rect.size.width - aPoint.y, rect.size.height - aPoint.x); 74 | 75 | case kRightTop: // untested 76 | return CGPointMake(aPoint.y, aPoint.x); 77 | case kRightBottom: // vetted - back 78 | return CGPointMake(rect.size.width - aPoint.y, rect.size.height - aPoint.x); 79 | } 80 | } 81 | 82 | CGRect RectInEXIF(ExifOrientation exifOrientation, CGRect inner, CGRect outer) 83 | { 84 | CGRect rect; 85 | rect.origin = PointInEXIF(exifOrientation, inner.origin, outer); 86 | rect.size = SizeInEXIF(exifOrientation, inner.size); 87 | 88 | switch(exifOrientation) 89 | { 90 | case kTopLeft: // vetted 91 | rect = CGRectOffset(rect, 0.0f, -inner.size.height); 92 | break; 93 | case kTopRight: // vetted 94 | rect = CGRectOffset(rect, -inner.size.width, -inner.size.height); 95 | break; 96 | 97 | case kBottomRight: // vetted 98 | rect = CGRectOffset(rect, -inner.size.width, 0.0f); 99 | break; 100 | case kBottomLeft: // vetted 101 | break; 102 | 103 | case kLeftTop: // vetted 104 | break; 105 | case kRightTop: // untested 106 | break; 107 | 108 | case kRightBottom: // vetted on back, NOT FOR FRONT 109 | rect = CGRectOffset(rect, -inner.size.width, -inner.size.height); 110 | break; 111 | case kLeftBottom: // untested 112 | break; 113 | } 114 | 115 | return rect; 116 | } -------------------------------------------------------------------------------- /Cam Pack/Orientation/ImageOrientation.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | #import 8 | 9 | // Correspondences Updated 3/31/14. Thanks Andrea, who pointed out I'd swapped out 6 and 8. 10 | 11 | /* 12 | 13 | HOME BUTTON CAMERA OUTPUT UNMIRRORED MIRRORED ORIENTATION 14 | Bottom Front Right Left Mirrored Right Portrait 15 | Right Front Down Down Mirrored Down LandscapeLeft 16 | Top Front Left Right Mirrored Left PortraitUpsideDown 17 | Left Front Up Up Mirrored Up LandscapeRight 18 | 19 | Bottom Back Right Right Left Mirrored Portrait 20 | Right Back Up Up Up Mirrored LandscapeLeft 21 | Top Back Left Left Right Mirrored PortraitUpsideDown 22 | Left Back Down Down Down Mirrored LandscapeRight 23 | 24 | AVAILABLE ORIENTATIONS: EXIF and UIImageOrientation 25 | 26 | topleft toprt botrt botleft leftop leftbot rightbot righttop 27 | EXIF 1 2 3 4 5 6 7 8 28 | 29 | XXXXXX XXXXXX XX XX XXXXXXXXXX XX XX XXXXXXXXXX 30 | XX XX XX XX XX XX XX XX XX XX XX XX 31 | XXXX XXXX XXXX XXXX XX XXXXXXXXXX XXXXXXXXXX XX 32 | XX XX XX XX 33 | XX XX XXXXXX XXXXXX 34 | 35 | UI 0 4 1 5 6 3 7 2 36 | up upmirror down downmir leftmir right rightmir left 37 | 38 | 39 | 40 | MAPPINGS BETWEEN ORIENTATIONS: 41 | 42 | {1, 3, 8, 6, 2, 4, 5, 7}; EXIF 43 | {0 1 2 3 4 5 6 7} UIIMG 44 | 45 | {1 2 3 4 5 6 7 8} EXIF 46 | {0, 4, 1, 5, 6, 3, 7, 2}; UIIMG 47 | 48 | */ 49 | 50 | // EXIF ORIENTATIONS 51 | typedef enum { 52 | kTopLeft = 1, // UIImageOrientationUp, (0,0) at top left 53 | kTopRight = 2, // UIImageOrientationUpMirrored, (0,0) at top right 54 | kBottomRight = 3, // UIImageOrientationDown (0,0) at bottom right 55 | kBottomLeft = 4, // UIImageOrientationDownMirrored (0,0) at bottom left 56 | kLeftTop = 5, // UIImageOrientationLeftMirrored (0,0) at left top 57 | kLeftBottom = 6, // UIImageOrientationLeft (0,0) at right top 58 | kRightBottom = 7, // UIImageOrientationRightMirrored (0,0) at right bottom 59 | kRightTop = 8 // UIImageOrientationRight (0,0) at left bottom 60 | } ExifOrientation; 61 | 62 | /* 63 | 64 | UIIMAGE ORIENTATIONS 65 | 66 | typedef enum { 67 | UIImageOrientationUp = 0, // 0 deg rotation exif 1 68 | UIImageOrientationDown = 1, // 180 deg rotation exif 3 69 | UIImageOrientationLeft = 2, // 90 deg CCW exif 6 70 | UIImageOrientationRight = 3, // 90 deg CW exif 8 71 | UIImageOrientationUpMirrored = 4, // horizontal flip exif 2 72 | UIImageOrientationDownMirrored = 5, // horizontal flip exif 4 73 | UIImageOrientationLeftMirrored = 6, // vertical flip exif 5 74 | UIImageOrientationRightMirrored = 7, // vertical flip exif 7 75 | } UIImageOrientation; 76 | */ 77 | 78 | /* 79 | 80 | DEVICE ORIENTATIONS 81 | 82 | UIDeviceOrientationUnknown 83 | The orientation of the device cannot be determined. 84 | UIDeviceOrientationPortrait 85 | The device is in portrait mode, with the device held upright and the home button at the bottom. 86 | UIDeviceOrientationPortraitUpsideDown 87 | The device is in portrait mode but upside down, with the device held upright and the home button at the top. 88 | UIDeviceOrientationLandscapeLeft 89 | The device is in landscape mode, with the device held upright and the home button on the right side. 90 | UIDeviceOrientationLandscapeRight 91 | The device is in landscape mode, with the device held upright and the home button on the left side. 92 | UIDeviceOrientationFaceUp 93 | The device is held parallel to the ground with the screen facing upwards. 94 | UIDeviceOrientationFaceDown 95 | The device is held parallel to the ground with the screen facing downwards. 96 | */ 97 | 98 | // UTILITY FUNCTIONS 99 | 100 | NSString *ImageOrientationNameFromOrientation(UIImageOrientation orientation); 101 | NSString *ImageOrientationName(UIImage *anImage); 102 | NSString *EXIFOrientationNameFromOrientation(uint orientation); 103 | NSString *DeviceOrientationName(UIDeviceOrientation orientation); 104 | NSString *CurrentDeviceOrientationName(); 105 | 106 | BOOL DeviceIsLandscape(); 107 | BOOL DeviceIsPortrait(); 108 | 109 | uint EXIFOrientationFromUIOrientation(UIImageOrientation uiorientation); 110 | UIImageOrientation ImageOrientationFromEXIFOrientation(uint exiforientation); 111 | 112 | UIImageOrientation CurrentImageOrientation(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip); 113 | uint CurrentEXIFOrientation(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip); 114 | 115 | // There is a huge bug in that portrait / back camera doesn't recognize 116 | // properly with its native EXIF alignment This function works around it 117 | // but you then have to adjust the geometry accordingly. Bah. 118 | uint DetectorEXIF(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip); 119 | -------------------------------------------------------------------------------- /Cam Pack/Orientation/ImageOrientation.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | 8 | #import "ImageOrientation.h" 9 | 10 | uint EXIFOrientationFromUIOrientation(UIImageOrientation uiorientation) 11 | { 12 | if (uiorientation > 7) return 1; 13 | int orientations[8] = {1, 3, 8, 6, 2, 4, 5, 7}; 14 | return orientations[uiorientation]; 15 | } 16 | 17 | UIImageOrientation ImageOrientationFromEXIFOrientation(uint exiforientation) 18 | { 19 | if ((exiforientation < 1) || (exiforientation > 8)) return UIImageOrientationUp; 20 | int orientations[8] = {0, 4, 1, 5, 6, 3, 7, 2}; 21 | return orientations[exiforientation]; 22 | } 23 | 24 | NSString *DeviceOrientationName(UIDeviceOrientation orientation) 25 | { 26 | NSArray *names = [NSArray 27 | arrayWithObjects: 28 | @"Unknown", 29 | @"Portrait", 30 | @"Portrait Upside Down", 31 | @"Landscape Left", 32 | @"Landscape Right", 33 | @"Face Up", 34 | @"Face Down", 35 | nil]; 36 | return [names objectAtIndex:orientation]; 37 | } 38 | 39 | NSString *CurrentDeviceOrientationName() 40 | { 41 | UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; 42 | return DeviceOrientationName(orientation); 43 | } 44 | 45 | NSString *ImageOrientationNameFromOrientation(UIImageOrientation orientation) 46 | { 47 | NSArray *names = [NSArray 48 | arrayWithObjects: 49 | @"Up", 50 | @"Down", 51 | @"Left", 52 | @"Right", 53 | @"Up-Mirrored", 54 | @"Down-Mirrored", 55 | @"Left-Mirrored", 56 | @"Right-Mirrored", 57 | nil]; 58 | return [names objectAtIndex:orientation]; 59 | } 60 | 61 | NSString *EXIFOrientationNameFromOrientation(uint orientation) 62 | { 63 | NSArray *names = [NSArray 64 | arrayWithObjects: 65 | @"Undefined", 66 | @"Top Left", 67 | @"Top Right", 68 | @"Bottom Right", 69 | @"Bottom Left", 70 | @"Left Top", 71 | @"Left Bottom", 72 | @"Right Bottom", 73 | @"Right Top", 74 | nil]; 75 | return [names objectAtIndex:orientation]; 76 | } 77 | 78 | 79 | NSString *ImageOrientationName(UIImage *anImage) 80 | { 81 | return ImageOrientationNameFromOrientation(anImage.imageOrientation); 82 | } 83 | 84 | BOOL DeviceIsLandscape() 85 | { 86 | UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; 87 | return UIDeviceOrientationIsLandscape(orientation); 88 | } 89 | 90 | BOOL DeviceIsPortrait() 91 | { 92 | UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; 93 | return UIDeviceOrientationIsPortrait(orientation); 94 | } 95 | 96 | UIImageOrientation CurrentImageOrientationWithMirroring(BOOL isUsingFrontCamera) 97 | { 98 | switch ([UIDevice currentDevice].orientation) 99 | { 100 | case UIDeviceOrientationPortrait: 101 | return isUsingFrontCamera ? UIImageOrientationRight : UIImageOrientationLeftMirrored; 102 | case UIDeviceOrientationPortraitUpsideDown: 103 | return isUsingFrontCamera ? UIImageOrientationLeft :UIImageOrientationRightMirrored; 104 | case UIDeviceOrientationLandscapeLeft: 105 | return isUsingFrontCamera ? UIImageOrientationDown : UIImageOrientationUpMirrored; 106 | case UIDeviceOrientationLandscapeRight: 107 | return isUsingFrontCamera ? UIImageOrientationUp : UIImageOrientationDownMirrored; 108 | default: 109 | return UIImageOrientationUp; 110 | } 111 | } 112 | 113 | // Expected Image orientation from current orientation and camera in use 114 | UIImageOrientation CurrentImageOrientation(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip) 115 | { 116 | if (shouldMirrorFlip) 117 | return CurrentImageOrientationWithMirroring(isUsingFrontCamera); 118 | 119 | switch ([UIDevice currentDevice].orientation) 120 | { 121 | case UIDeviceOrientationPortrait: 122 | return isUsingFrontCamera ? UIImageOrientationLeftMirrored : UIImageOrientationRight; 123 | case UIDeviceOrientationPortraitUpsideDown: 124 | return isUsingFrontCamera ? UIImageOrientationRightMirrored :UIImageOrientationLeft; 125 | case UIDeviceOrientationLandscapeLeft: 126 | return isUsingFrontCamera ? UIImageOrientationDownMirrored : UIImageOrientationUp; 127 | case UIDeviceOrientationLandscapeRight: 128 | return isUsingFrontCamera ? UIImageOrientationUpMirrored :UIImageOrientationDown; 129 | default: 130 | return UIImageOrientationUp; 131 | } 132 | } 133 | 134 | uint CurrentEXIFOrientation(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip) 135 | { 136 | return EXIFOrientationFromUIOrientation(CurrentImageOrientation(isUsingFrontCamera, shouldMirrorFlip)); 137 | } 138 | 139 | // Does not take camera into account for both portrait orientations 140 | // This is likely due to an ongoing bug 141 | uint DetectorEXIF(BOOL isUsingFrontCamera, BOOL shouldMirrorFlip) 142 | { 143 | if (isUsingFrontCamera || DeviceIsLandscape()) 144 | return CurrentEXIFOrientation(isUsingFrontCamera, shouldMirrorFlip); 145 | 146 | // Only back camera portrait or upside down here. This bugs me a lot. 147 | // Detection happens but the geometry is messed. 148 | int orientation = CurrentEXIFOrientation(!isUsingFrontCamera, shouldMirrorFlip); 149 | return orientation; 150 | } 151 | -------------------------------------------------------------------------------- /Cam Pack/UIImage+CoreImage/UIImage+CoreImage.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | @import Foundation; 8 | @import UIKit; 9 | 10 | // Used by multiple packs 11 | @interface UIImage (CoreImageUtility) 12 | + (UIImage *) imageWithCIImage: (CIImage *) aCIImage orientation: (UIImageOrientation) anOrientation; 13 | @property (nonatomic, readonly) CIImage *coreImageRepresentation; 14 | @end 15 | -------------------------------------------------------------------------------- /Cam Pack/UIImage+CoreImage/UIImage+CoreImage.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | #import "UIImage+CoreImage.h" 8 | 9 | @implementation UIImage (CoreImageUtility) 10 | - (CIImage *) coreImageRepresentation 11 | { 12 | if (self.CIImage) 13 | return self.CIImage; 14 | return [CIImage imageWithCGImage:self.CGImage]; 15 | } 16 | 17 | + (UIImage *) imageWithCIImage: (CIImage *) aCIImage orientation: (UIImageOrientation) anOrientation 18 | { 19 | if (!aCIImage) return nil; 20 | 21 | CGImageRef imageRef = [[CIContext contextWithOptions:nil] createCGImage:aCIImage fromRect:aCIImage.extent]; 22 | UIImage *image = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:anOrientation]; 23 | CFRelease(imageRef); 24 | 25 | return image; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /do-git: -------------------------------------------------------------------------------- 1 | #! /bin/csh -f 2 | 3 | if ($#argv != 1) then 4 | echo "Usage $0 commit-message" 5 | exit 1 6 | endif 7 | 8 | rm -rf build 9 | rm .DS_Store 10 | rm */.DS_Store 11 | rm -rf *.xcodeproj/ericasadun.* 12 | git add -A 13 | git commit -m "$1" 14 | git push 15 | --------------------------------------------------------------------------------