
Live Effects is an advanced real-time camera application that lets you view fun, crazy and sometimes artistic effects live through your iPhone or camera equipped iTouch.
“What you see is what you get” when you use the cool effects and overlays in Live Effects to take fun snapshots with your built-in camera. Contort your smile with the Shape effects. Add an artistic look with the Pixel Effects. View yourself hanging in an art museum with the Overlay Effects.
When you like what you see, take a snapshot. All snapshots are instantly saved in your iOS Photo Library.
We’ve developed an advanced implementation of technologies that combine your iOS device camera, pixel mapping and Open/GL to give Live Effects the unique ability to produce stunning image effects in REAL-TIME.
Experiment with all the effects and combinations to transform your look instantly. Then, take a snapshot and send them to your friends, use them as your profile pics or add them to your Address Book.
Sample Augmented Reality Code
This is all the code needed to build the foundation of an augmented reality app. This project allows you to implement a normal UIView as your camera layer or an Open/GLES1 view with the camera stream as a texture
GLView.h
//
// GLView.h
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#define HIRESDEVICE (((int)rintf([[[UIScreen mainScreen] currentMode] size].width/[[UIScreen mainScreen] bounds].size.width )>1))
#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
@interface GLView : UIView
{
@private
/* The pixel dimensions of the backbuffer */
GLint backingWidth;
GLint backingHeight;
EAGLContext *context;
/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
GLuint viewRenderbuffer;
GLuint viewFramebuffer;
GLuint depthRenderbuffer;
/* OpenGL name for the sprite texture */
GLuint spriteTexture;
}
@property (readonly) GLint backingWidth;
@property (readonly) GLint backingHeight;
@property (readonly) EAGLContext *context;
- (void) drawView;
+ (UIImage *) snapshot:(GLView *)eaglview;
@end
GLView.m
//
// GLView.m
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)
#import "GLView.h"
// Private Methods
//
@interface GLView()
- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;
@end
@implementation GLView
@synthesize backingWidth;
@synthesize backingHeight;
@synthesize context;
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id)init
{
self = [[super init] initWithFrame:CGRectMake(0.0, 0.0, 480.0, 640.0)]; // size of the camera image being captures
if ( self==nil )
return self;
// Set Content Scaling
//
if ( HIRESDEVICE )
{
self.contentScaleFactor = (CGFloat)2.0;
}
// Get our backing layer
//
CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
// Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
//
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil];
// Create our EAGLContext, and if successful make it current and create our framebuffer.
//
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer])
{
[self release];
return nil;
}
// Final View Settings
//
[self setOpaque:YES];
self.multipleTouchEnabled = YES;
self.backgroundColor = [UIColor clearColor];
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat zNear = 1.0;
GLfloat zFar = 1000.0;
GLfloat fieldOfView = 90; // Lens Angle of View
GLfloat size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = CGRectMake( (CGFloat)0.0, (CGFloat)0.0, backingWidth, backingHeight);
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / (rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, backingWidth, backingHeight);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_MULTISAMPLE);
glEnable(GL_LINE_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glDisable(GL_ALPHA_TEST);
// Turn Translucent Textures: OFF
//
glDisable(GL_BLEND);
// // Turn Translucent Textures: ON
// //
// glEnable(GL_BLEND);
// glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
return self;
}
- (void) drawView
{
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
- (BOOL)createFramebuffer
{
// Generate IDs for a framebuffer object and a color renderbuffer
//
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
// This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
// allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view).
//
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
// If this app uses a depth buffer, we'll create and attach one via another renderbuffer.
//
if ( YES )
{
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
{
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}
return YES;
}
- (void) destroyFramebuffer
{
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
if(depthRenderbuffer)
{
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}
+ (UIImage *) snapshot:(GLView *)eaglview
{
NSInteger x = 0;
NSInteger y = 0;
NSInteger width = [eaglview backingWidth];
NSInteger height = [eaglview backingHeight];
NSInteger dataLength = width * height * 4;
NSUInteger i;
for ( i=0; i<100; i++ )
{
glFlush();
CFRunLoopRunInMode(kCFRunLoopDefaultMode, (float)1.0/(float)60.0, FALSE);
}
GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
// Read pixel data from the framebuffer
//
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
// Create a CGImage with the pixel data
// If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
// otherwise, use kCGImageAlphaPremultipliedLast
//
CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, ref, NULL, true, kCGRenderingIntentDefault);
// OpenGL ES measures data in PIXELS
// Create a graphics context with the target size measured in POINTS
//
NSInteger widthInPoints;
NSInteger heightInPoints;
if (NULL != UIGraphicsBeginImageContextWithOptions)
{
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// Set the scale parameter to your OpenGL ES view's contentScaleFactor
// so that you get a high-resolution snapshot when its value is greater than 1.0
//
CGFloat scale = eaglview.contentScaleFactor;
widthInPoints = width / scale;
heightInPoints = height / scale;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale);
}
else
{
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
//
widthInPoints = width;
heightInPoints = height;
UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints));
}
CGContextRef cgcontext = UIGraphicsGetCurrentContext();
// UIKit coordinate system is upside down to GL/Quartz coordinate system
// Flip the CGImage by rendering it to the flipped bitmap context
// The size of the destination area is measured in POINTS
//
CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
// Retrieve the UIImage from the current context
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // autoreleased image
UIGraphicsEndImageContext();
// Clean up
free(data);
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);
return image;
}
@end
GLMapper.h
//
// GLMapper.h
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "GLView.h"
@interface GLMapper : NSObject
{
// VERTICIES
//
GLfloat modelScale;
int vertexCount;
GLfloat *vertexData;
GLfloat *textureData;
}
@property (readonly) int vertexCount;
@property (readonly) GLfloat *vertexData;
@property (readonly) GLfloat *textureData;
- (void) buildVertexMap:(NSUInteger)buildOptions;
- (void) buildVertexMap:(NSUInteger)buildOptions scale:(GLfloat)scaleOption;
- (void) buildTextureMap:(NSUInteger)buildOptions forOrientation:(UIInterfaceOrientation)toInterfaceOrientation frontCamera:(BOOL)useFrontCamera;
@end
GLMapper.m
//
// GLMapper.m
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "GLMapper.h"
static CGFloat aspectAdjustment = 1.333;
// FOUR PANELS
//
static int planeDefVertexCount=24;
static const GLfloat planeDef[] =
{
-1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
};
@implementation GLMapper
@synthesize vertexCount;
@synthesize vertexData;
@synthesize textureData;
- (void) dealloc
{
if ( vertexData != NULL )
{
free( vertexData );
vertexData=NULL;
}
if ( textureData != NULL )
{
free( textureData );
textureData=NULL;
}
[super dealloc];
}
- (id) init
{
self = [super init];
vertexCount=0;
vertexData=NULL;
textureData=NULL;
return self;
}
- (void) buildTextureMap:(NSUInteger)buildOptions forOrientation:(UIInterfaceOrientation)toInterfaceOrientation frontCamera:(BOOL)frontCamera
{
int i;
int textureIndex = 0;
int vertexIndex = 0;
GLfloat xMinVertex = FLT_MAX;
GLfloat yMinVertex = FLT_MAX;
GLfloat xMaxVertex = -FLT_MAX;
GLfloat yMaxVertex = -FLT_MAX;
if ( textureData != NULL )
{
free( textureData );
textureData=NULL;
}
textureData = (GLfloat *)malloc(sizeof(GLfloat) * vertexCount * 2);
memset(textureData, (unsigned int)0, sizeof(GLfloat) * vertexCount * 2);
for ( i=0; i<vertexCount*3; i+=3 )
{
if ( vertexData[i+0] < xMinVertex )
xMinVertex = vertexData[i+0] ;
if ( vertexData[i+1] < yMinVertex )
yMinVertex = vertexData[i+1] ;
if ( vertexData[i+0] > xMaxVertex )
xMaxVertex = vertexData[i+0] ;
if ( vertexData[i+1] > yMaxVertex )
yMaxVertex = vertexData[i+1] ;
}
GLfloat xVertexRange = fabs(xMaxVertex - xMinVertex);
GLfloat yVertexRange = fabs(yMaxVertex - yMinVertex);
double vertexPctX;
double vertexPctY;
double texturePointX;
double texturePointY;
double texturePointXsizeAdjusted;
double texturePointYsizeAdjusted;
GLfloat xMinTexture = FLT_MAX;
GLfloat yMinTexture = FLT_MAX;
GLfloat xMaxTexture = -FLT_MAX;
GLfloat yMaxTexture = -FLT_MAX;
//
// TEXTURE COORDINATES
//
for ( i=0; i<vertexCount; i++ )
{
vertexIndex = i*3;
textureIndex = i*2;
vertexPctX = (double)((vertexData[vertexIndex+0] - xMinVertex) / xVertexRange);
vertexPctY = (double)((vertexData[vertexIndex+1] - yMinVertex) / yVertexRange);
if ( frontCamera )
{
texturePointX = (double)1024.0 - (double)vertexPctY * (double)1024.0;
texturePointY = (double)vertexPctX * (double)1024.0;
}
else
{
texturePointX = (double)1024.0 - (double)vertexPctY * (double)1024.0;
texturePointY = (double)1024.0 - (double)vertexPctX * (double)1024.0;
}
texturePointXsizeAdjusted = texturePointX * (double)((double)640.0 / (double)1024.0);
texturePointYsizeAdjusted = texturePointY * (double)((double)480.0 / (double)1024.0) + (double)544.0;
textureData[textureIndex+0] = (GLfloat)texturePointXsizeAdjusted/1024.0;
textureData[textureIndex+1] = (GLfloat)texturePointYsizeAdjusted/1024.0;
if ( textureData[textureIndex+0] < xMinTexture )
xMinTexture = textureData[textureIndex+0] ;
if ( textureData[textureIndex+1] < yMinTexture )
yMinTexture = textureData[textureIndex+1] ;
if ( textureData[textureIndex+0] > xMaxTexture )
xMaxTexture = textureData[textureIndex+0] ;
if ( textureData[textureIndex+1] > yMaxTexture )
yMaxTexture = textureData[textureIndex+1] ;
}
}
- (void) buildVertexMap:(NSUInteger)buildOptions
{
[self buildVertexMap:buildOptions scale:(GLfloat)1.0];
}
- (void) buildVertexMap:(NSUInteger)buildOptions scale:(GLfloat)scaleOption
{
int i;
int vertexIndex = 0;
if ( vertexData != NULL )
{
free( vertexData );
vertexData=NULL;
}
vertexCount=0;
modelScale = (GLfloat)1.0;
if ( scaleOption > (GLfloat)0.0 )
modelScale = scaleOption;
GLfloat xScale = modelScale;
GLfloat yScale = modelScale * aspectAdjustment;
GLfloat zScale = modelScale;
vertexCount = planeDefVertexCount;
vertexData = (GLfloat *)malloc( sizeof(GLfloat) * vertexCount * 3 );
memset( vertexData, (unsigned char)0, sizeof(GLfloat) * vertexCount * 3 );
for ( i=0; i<vertexCount*3; i+=3 )
{
vertexData[vertexIndex+0] = planeDef[i+0] * xScale;
vertexData[vertexIndex+1] = planeDef[i+1] * yScale;
vertexData[vertexIndex+2] = planeDef[i+2] * zScale;
vertexIndex+=3;
}
}
@end
AppDelegate.h
//
// SuperBoothAppDelegate.h
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import <UIKit/UIKit.h>
#import "ViewController.h"
@interface AppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
ViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
AppDelegate.m
//
// SuperBoothAppDelegate.m
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application setIdleTimerDisabled:YES];
[application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
[window setBackgroundColor:[UIColor blackColor]];
viewController = [[ViewController alloc] init];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc
{
[viewController release];
[window release];
[super dealloc];
}
@end
ViewController.h
//
// ViewController.h
// AugCam
//
// Created by John Carter on 1/26/2012.
//
enum {
VIEWSOURCE_UIIMAGEVIEW = 1,
VIEWSOURCE_OPENGLVIEW,
};
enum {
CameraDeviceSetting640x480 = 0,
CameraDeviceSettingHigh = 1,
CameraDeviceSettingMedium = 2,
CameraDeviceSettingLow = 3,
};
#import <UIKit/UIKit.h>
#import <MobileCoreServices/UTCoreTypes.h>
#import <MobileCoreServices/UTType.h>
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreVideo/CoreVideo.h>
#import <CoreMedia/CoreMedia.h>
#import "GLView.h"
#import "GLMapper.h"
@interface ViewController : UIViewController <AVCaptureVideoDataOutputSampleBufferDelegate>
{
BOOL ignoreImageStream;
// Device Information
//
int cameraCount;
int currentCameraDeviceIndex;
int frontCameraDeviceIndex;
int backCameraDeviceIndex;
int cameraImageOrientation;
UIInterfaceOrientation currentDeviceOrientation;
// test layers for augmented screen capture
//
UIImageView *backingLayer;
UIImageView *overlayLayer;
// The primary View for image capture
// and it's cooresponding view for display
//
int currentViewSource; // 1=cameraView, 2=glView
UIView *displayView;
// ViewSettings
//
NSUInteger textureMapBuilderCode;
NSUInteger vertexMapBuilderCode;
GLfloat vertexMapBuilderScale;
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Camera Capture, OpenGL and Pixel Manipulation Stuff
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
AVCaptureDeviceInput *captureInput;
AVCaptureVideoDataOutput *captureOutput;
AVCaptureSession *captureSession;
AVCaptureVideoPreviewLayer *cameraPreviewLayer;
NSDictionary* videoSettings;
NSNumber *pixelFormatCode;
NSString *pixelFormatKey;
int cameraDeviceSetting;
UIImageView *cameraView;
UIImage *cameraImage;
UIImage *cameraImage0;
UIImage *cameraImage1;
// Open/GL
//
GLView *glView;
EAGLContext *glViewContext;
// VERTICIES
//
GLMapper *glMapper;
int vertexCount;
GLfloat *vertexData;
GLfloat *textureData;
// CAMERA IMAGE TEXTURE BUFFER
//
GLuint cameraTextureTag;
void *cameraTextureImageBuffer;
CGRect cameraTextureRect;
CGContextRef cameraTextureContext;
CGColorSpaceRef cameraTextureColorSpace;
// VERTEX BUFFERING
//
GLuint vboVertexBuffer;
GLuint vboTextureBuffer;
}
@property (readonly) UIImage *cameraImage;
@property (readonly) GLView *glView;
@property (readonly) int cameraImageOrientation;
@end
ViewController.m
//
// ViewController.m
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "ViewController.h"
#import "ScreenCapture.h"
@interface ViewController()
- (void) performImageCaptureFrom:(CMSampleBufferRef)sampleBuffer;
- (void) activateCameraFeed;
- (void) initOpenGL;
- (void) refreshVBObjects;
- (void) scanForCameraDevices;
- (void) mapGLViewForOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
- (void) swapCameras;
- (void) setViewSource:(int)newViewSource;
- (void) saveDisplayView;
- (void) saveGLView;
- (void) goLive;
@end
@implementation ViewController
@synthesize cameraImage;
@synthesize glView;
@synthesize cameraImageOrientation;
- (void)dealloc
{
if ( captureOutput != nil )
{
[captureOutput release];
captureOutput = nil;
}
if ( captureInput != nil )
{
[captureInput release];
captureInput = nil;
}
if ( glMapper != nil )
{
[glMapper release];
glMapper = nil;
}
if ( glView != nil )
{
[glView removeFromSuperview];
[glView release];
glView = nil;
}
if ( cameraView != nil )
{
[cameraView removeFromSuperview];
[cameraView release];
cameraView = nil;
}
if (backingLayer != nil)
{
[backingLayer removeFromSuperview];
[backingLayer release];
backingLayer=nil;
}
if (overlayLayer != nil)
{
[overlayLayer removeFromSuperview];
[overlayLayer release];
overlayLayer=nil;
}
if (displayView != nil)
{
[displayView removeFromSuperview];
[displayView release];
displayView=nil;
}
[super dealloc];
}
- (id) init
{
self = [super init];
[[self view] setBackgroundColor:[UIColor blackColor]];
return self;
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self goLive];
}
- (void) goLive
{
// Disable Processing any Camera Fees for now
//
ignoreImageStream = YES;
// Device Info
//
currentDeviceOrientation = UIInterfaceOrientationPortrait;
currentViewSource = 0; // or VIEWSOURCE_UIIMAGEVIEW or VIEWSOURCE_OPENGLVIEW
// Scan for Cameras
//
cameraCount = 0;
frontCameraDeviceIndex = -1;
backCameraDeviceIndex = -1;
[self scanForCameraDevices];
// Turn on the Camera
//
[self activateCameraFeed];
// Init Open/GL
//
[self initOpenGL];
vertexMapBuilderScale = (GLfloat)1.0;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -1.0);
// testing gl manipulation
//
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -1.5);
glRotatef( (GLfloat)M_PI, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0 );
glRotatef( (GLfloat)M_PI, (GLfloat)0.0, (GLfloat)1.0, (GLfloat)0.0 );
glRotatef( (GLfloat)M_PI, (GLfloat)0.0, (GLfloat)0.0, (GLfloat)1.0 );
[self mapGLViewForOrientation:UIInterfaceOrientationPortrait];
// Init the test layers
//
backingLayer = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"backinglayer.png"]];
overlayLayer = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"overlaylayer.png"]];
// Init the Plain Camera
//
cameraView = [[UIImageView alloc] initWithFrame:CGRectMake((CGFloat)0.0, (CGFloat)0.0, (CGFloat)480.0, (CGFloat)640.0)]; // size of the camera feed images
// Init the displayView container and set the transform to scale the 480x640 so it will fit in 320x480 view (becomes 320x427)
//
displayView = [[UIImageView alloc] initWithFrame:CGRectMake((CGFloat)0.0, (CGFloat)0.0, (CGFloat)480.0, (CGFloat)640.0)]; // size of the camera feed images
[displayView setTransform:CGAffineTransformIdentity];
[displayView setTransform:CGAffineTransformScale([displayView transform], (CGFloat)0.667, (CGFloat)0.667)];
[displayView setTransform:CGAffineTransformTranslate([displayView transform], (CGFloat)-120, (CGFloat)-173)];
[self.view addSubview:displayView];
// Set the default view source (also turns on the image stream)
//
// [self setViewSource:VIEWSOURCE_UIIMAGEVIEW];
[self setViewSource:VIEWSOURCE_OPENGLVIEW];
}
- (void) setViewSource:(int)newViewSource
{
if ( newViewSource==currentViewSource )
{
return;
}
ignoreImageStream=YES;
// Pop all the views off the container view
//
if ( overlayLayer != nil )
[overlayLayer removeFromSuperview];
switch( currentViewSource )
{
case VIEWSOURCE_OPENGLVIEW:
[glView removeFromSuperview];
break;
case VIEWSOURCE_UIIMAGEVIEW:
[cameraView removeFromSuperview];
break;
}
if ( backingLayer != nil )
[backingLayer removeFromSuperview];
currentViewSource = newViewSource;
// Put all the views back in the container view
//
if ( backingLayer != nil )
[displayView addSubview:backingLayer];
switch( newViewSource )
{
case VIEWSOURCE_OPENGLVIEW:
[displayView addSubview:glView];
break;
case VIEWSOURCE_UIIMAGEVIEW:
[displayView addSubview:cameraView];
break;
}
if ( overlayLayer != nil )
[displayView addSubview:overlayLayer];
ignoreImageStream=NO;
}
//
// This will only automatically change the size of self.view
// it will not change the sizes and locations of subviews
//
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return( interfaceOrientation==UIInterfaceOrientationPortrait );
}
//
// This method releases the 'newimage' because it may change it
// so the references passed can no longer be released by the
// caller of this method
//
- (void) newCameraImageNotification:(CGImageRef)newImage
{
if ( newImage == nil )
return;
if ( currentViewSource == VIEWSOURCE_UIIMAGEVIEW )
{
cameraImage0 = [[UIImage alloc] initWithCGImage:newImage scale:(CGFloat)1.0 orientation:cameraImageOrientation];
cameraImage = cameraImage0;
[cameraView setImage:cameraImage];
if ( cameraImage1 != nil )
[cameraImage1 release];
cameraImage1 = cameraImage0;
}
else
if ( currentViewSource == VIEWSOURCE_OPENGLVIEW )
{
size_t textureBufferWidth = (size_t)rintf( cameraTextureRect.size.width );
size_t textureBufferHeight = (size_t)rintf( cameraTextureRect.size.height );
// memset( cameraTextureImageBuffer, (unsigned char)0, textureBufferWidth * textureBufferHeight * 4 ); // needs to be done to avoid shadowing under transparency
// Draw the full image into our cameraTextureImageBuffer (which is sized to account for this) even though
// for texture mapping to work, we will only bind to a 512x512 section of the image
//
CGRect extractRect = CGRectMake((CGFloat)0.0, (CGFloat)0.0, (CGFloat)CGImageGetWidth(newImage), (CGFloat)CGImageGetHeight(newImage));
CGContextDrawImage(cameraTextureContext, extractRect, newImage);
glBindTexture(GL_TEXTURE_2D, cameraTextureTag);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureBufferWidth, textureBufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, cameraTextureImageBuffer);
[self refreshVBObjects];
}
CGImageRelease(newImage);
}
- (void) savedSnapShot:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if ( error != nil )
{
NSLog(@"Error saving snapshot: %@", );
}
}
- (void) snapShot
{
if ( ignoreImageStream )
return;
ignoreImageStream = YES;
if ( currentViewSource==VIEWSOURCE_OPENGLVIEW )
[self saveGLView];
else
if ( currentViewSource==VIEWSOURCE_UIIMAGEVIEW )
[self saveDisplayView];
ignoreImageStream = NO;
}
- (void) saveGLView
{
UIImage *photo = [ScreenCapture GLViewToImage:glView]; // returns an autoreleased image
if ( photo == nil )
return;
[photo retain];
// Replace CAEAGLLayer glView with a UIImageView version of itself
//
NSInteger viewIndex = [[displayView subviews] indexOfObject:glView];
UIImageView *glImageView = [[UIImageView alloc] initWithImage:photo];
[glView removeFromSuperview];
[displayView insertSubview:glImageView atIndex:viewIndex];
[photo release];
// Take a normal snapshot now
//
[self saveDisplayView];
// Put glView back and release the temporary UIImageView version of it
//
[glImageView removeFromSuperview];
[glImageView release];
[displayView insertSubview:glView atIndex:viewIndex];
}
- (void) saveDisplayView
{
UIImage *photo = [ScreenCapture UIViewToImage:displayView]; // returns an autoreleased image
if ( photo == nil )
return;
[photo retain];
UIImageWriteToSavedPhotosAlbum(photo, self, @selector(savedSnapShot:didFinishSavingWithError:contextInfo:), nil);
[photo release];
}
- (void) refreshVBObjects
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, vboVertexBuffer);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, (void *)0);
glBindBuffer(GL_ARRAY_BUFFER, vboTextureBuffer);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, cameraTextureTag);
glTexCoordPointer(2, GL_FLOAT, 0, (void *)0);
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
[glViewContext presentRenderbuffer:GL_RENDERBUFFER_OES];
}
- (void) mapGLViewForOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if ( vboVertexBuffer != 0 )
{
glDeleteBuffers( 1, &vboVertexBuffer );
glDeleteBuffers( 1, &vboTextureBuffer );
}
[glMapper buildVertexMap:vertexMapBuilderCode scale:vertexMapBuilderScale];
[glMapper buildTextureMap:textureMapBuilderCode forOrientation:toInterfaceOrientation frontCamera:(currentCameraDeviceIndex==frontCameraDeviceIndex)];
vertexCount = [glMapper vertexCount];
vertexData = [glMapper vertexData];
textureData = [glMapper textureData];
glGenBuffers(1, &vboVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vboVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertexCount*3, vertexData, GL_STATIC_DRAW);
glGenBuffers(1, &vboTextureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vboTextureBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertexCount*2, textureData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
- (void) initOpenGL
{
glView = [[GLView alloc] init];
glViewContext = [glView context];
glMapper = [[GLMapper alloc] init];
//
// Establish Location of Camera
//
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -1.0);
cameraTextureRect = CGRectMake( (CGFloat)0.0, (CGFloat)0.0, (CGFloat)1024.0, (CGFloat)1024.0 );
size_t imageWidth = (size_t)rintf( cameraTextureRect.size.width );
size_t imageHeight = (size_t)rintf( cameraTextureRect.size.height );
//
// To increase performance, the full captured image is copied to this buffer so the
// size of the buffer needs to match the size of the images (480x640) even though
// for texture mapping we use a 512x512 section of the date which equates to
// chopping the final texture mapped image to a 480x546 image.
//
// The final 480x546 image still has a ratio of 480x640 so after it is mapped
// as a 512x512 texture the texture mapping needs to account for the
// perspective correction
//
unsigned int dataSize = 960 * 1280 * 4;
cameraTextureImageBuffer = malloc( dataSize );
memset( cameraTextureImageBuffer, (unsigned char)0, dataSize );
// Create Texture Colorspace
//
cameraTextureColorSpace = CGColorSpaceCreateDeviceRGB();
// Create the texture Context
cameraTextureContext = CGBitmapContextCreate(cameraTextureImageBuffer, imageWidth, imageHeight, 8, imageWidth*4, cameraTextureColorSpace, kCGImageAlphaPremultipliedLast);
if ( cameraTextureContext == NULL )
{
NSLog(@"error creating texture context");
return;
}
glGenTextures(1, &cameraTextureTag);
glBindTexture(GL_TEXTURE_2D, cameraTextureTag);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glEnable(GL_TEXTURE_2D);
}
- (void) scanForCameraDevices
{
cameraCount = 0;
frontCameraDeviceIndex = -1;
backCameraDeviceIndex = -1;
AVCaptureDevice *backCameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSArray *deviceList = [AVCaptureDevice devices];
NSRange cameraSearch;
NSUInteger i;
for ( i=0; i<[deviceList count]; i++ )
{
AVCaptureDevice *currentDevice = (AVCaptureDevice *)[deviceList objectAtIndex:i];
//
// This is the best info so skip string the string searches
// that follow if we have a match on this
//
if ( currentDevice==backCameraDevice )
{
backCameraDeviceIndex = i;
cameraCount++;
continue;
}
cameraSearch = [[currentDevice description] rangeOfString:@"front camera" options:NSCaseInsensitiveSearch];
if ( frontCameraDeviceIndex<0 && cameraSearch.location != NSNotFound )
{
frontCameraDeviceIndex = i;
cameraCount++;
continue;
}
cameraSearch = [[currentDevice description] rangeOfString:@"back camera" options:NSCaseInsensitiveSearch];
if ( backCameraDevice<0 && cameraSearch.location != NSNotFound )
{
backCameraDeviceIndex = i;
cameraCount++;
continue;
}
cameraSearch = [[currentDevice description] rangeOfString:@"camera" options:NSCaseInsensitiveSearch];
if ( backCameraDevice<0 && cameraSearch.location != NSNotFound )
{
backCameraDeviceIndex = i;
cameraCount++;
continue;
}
}
}
- (void) activateCameraFeed
{
videoSettings = nil;
pixelFormatCode = [[NSNumber alloc] initWithUnsignedInt:(unsigned int)kCVPixelFormatType_32BGRA];
pixelFormatKey = [[NSString alloc] initWithString:(NSString *)kCVPixelBufferPixelFormatTypeKey];
videoSettings = [[NSDictionary alloc] initWithObjectsAndKeys:pixelFormatCode, pixelFormatKey, nil];
dispatch_queue_t queue = dispatch_queue_create("com.jellyfilledstudios.ImageCaptureQueue", NULL);
captureOutput = [[AVCaptureVideoDataOutput alloc] init];
[captureOutput setAlwaysDiscardsLateVideoFrames:YES];
[captureOutput setSampleBufferDelegate:self queue:queue];
[captureOutput setVideoSettings:videoSettings];
dispatch_release(queue);
AVCaptureDevice *selectedCamera;
currentCameraDeviceIndex = -1;
// default to front facing camera
//
if ( YES )
{
currentCameraDeviceIndex = frontCameraDeviceIndex;
cameraImageOrientation = UIImageOrientationLeftMirrored;
}
else
{
currentCameraDeviceIndex = backCameraDeviceIndex;
cameraImageOrientation = UIImageOrientationRight;
}
if ( currentCameraDeviceIndex < 0 )
selectedCamera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
else
selectedCamera = [[AVCaptureDevice devices] objectAtIndex:(NSUInteger)currentCameraDeviceIndex];
captureInput = [AVCaptureDeviceInput deviceInputWithDevice:selectedCamera error:nil];
if ( [selectedCamera lockForConfiguration:nil] )
{
if ( [selectedCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure] )
{
[selectedCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if ( [selectedCamera isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance] )
{
[selectedCamera setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
}
if ( [selectedCamera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus] )
{
[selectedCamera setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if ( [selectedCamera isTorchModeSupported:AVCaptureTorchModeAuto] )
{
[selectedCamera setTorchMode:AVCaptureTorchModeOff]; // AVCaptureTorchModeOn turns the "torch" light ON
}
if ( [selectedCamera isFlashModeSupported:AVCaptureFlashModeAuto] )
{
[selectedCamera setFlashMode:AVCaptureFlashModeAuto]; // AVCaptureFlashModeAuto
}
[selectedCamera unlockForConfiguration];
}
captureSession = [[AVCaptureSession alloc] init];
[captureSession beginConfiguration];
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480])
{
cameraDeviceSetting = CameraDeviceSetting640x480;
[captureSession setSessionPreset:AVCaptureSessionPreset640x480]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetHigh] )
{
cameraDeviceSetting = CameraDeviceSettingHigh;
[captureSession setSessionPreset:AVCaptureSessionPresetHigh]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetMedium] )
{
cameraDeviceSetting = CameraDeviceSettingMedium;
[captureSession setSessionPreset:AVCaptureSessionPresetMedium]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetLow] )
{
cameraDeviceSetting = CameraDeviceSettingLow;
[captureSession setSessionPreset:AVCaptureSessionPresetLow]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
[captureSession addInput:captureInput];
[captureSession addOutput:captureOutput];
[captureSession commitConfiguration];
[captureSession startRunning];
}
- (void)cameraSwapAnimation:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
}
- (void) swapCameras
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.75];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(cameraSwapAnimation:finished:context:)];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:[self view] cache:YES];
[UIView commitAnimations];
[captureSession stopRunning];
[captureSession beginConfiguration];
[captureSession removeInput:captureInput];
if ( currentCameraDeviceIndex==frontCameraDeviceIndex )
{
currentCameraDeviceIndex = backCameraDeviceIndex;
cameraImageOrientation = UIImageOrientationRight;
}
else
{
currentCameraDeviceIndex = frontCameraDeviceIndex;
cameraImageOrientation = UIImageOrientationLeftMirrored;
}
//
// Re-map the Open/GL to account for imageOrientation change
//
[self mapGLViewForOrientation:UIInterfaceOrientationPortrait];
// Start the Camera
//
AVCaptureDevice *selectedCamera = [[AVCaptureDevice devices] objectAtIndex:(NSUInteger)currentCameraDeviceIndex];
captureInput = [AVCaptureDeviceInput deviceInputWithDevice:selectedCamera error:nil];
if ( [selectedCamera lockForConfiguration:nil] )
{
if ( [selectedCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure] )
{
[selectedCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if ( [selectedCamera isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance] )
{
[selectedCamera setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
}
if ( [selectedCamera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus] )
{
[selectedCamera setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if ( [selectedCamera isTorchModeSupported:AVCaptureTorchModeAuto] )
{
[selectedCamera setTorchMode:AVCaptureTorchModeOff]; // AVCaptureTorchModeOn turns the "torch" light ON
}
if ( [selectedCamera isFlashModeSupported:AVCaptureFlashModeAuto] )
{
[selectedCamera setFlashMode:AVCaptureFlashModeAuto]; // AVCaptureFlashModeAuto
}
[selectedCamera unlockForConfiguration];
}
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480])
{
cameraDeviceSetting = CameraDeviceSetting640x480;
[captureSession setSessionPreset:AVCaptureSessionPreset640x480]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetHigh] )
{
cameraDeviceSetting = CameraDeviceSettingHigh;
[captureSession setSessionPreset:AVCaptureSessionPresetHigh]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetMedium] )
{
cameraDeviceSetting = CameraDeviceSettingMedium;
[captureSession setSessionPreset:AVCaptureSessionPresetMedium]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
else
if ( [selectedCamera supportsAVCaptureSessionPreset:AVCaptureSessionPresetLow] )
{
cameraDeviceSetting = CameraDeviceSettingLow;
[captureSession setSessionPreset:AVCaptureSessionPresetLow]; // AVCaptureSessionPresetHigh or AVCaptureSessionPreset640x480
}
[captureSession addInput:captureInput];
[captureSession commitConfiguration];
[captureSession startRunning];
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if ( ignoreImageStream )
return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performImageCaptureFrom:sampleBuffer];
[pool drain];
}
- (void) performImageCaptureFrom:(CMSampleBufferRef)sampleBuffer
{
CVImageBufferRef imageBuffer;
if ( CMSampleBufferGetNumSamples(sampleBuffer) != 1 )
return;
if ( !CMSampleBufferIsValid(sampleBuffer) )
return;
if ( !CMSampleBufferDataIsReady(sampleBuffer) )
return;
imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if ( CVPixelBufferGetPixelFormatType(imageBuffer) != kCVPixelFormatType_32BGRA )
return;
CVPixelBufferLockBaseAddress(imageBuffer,0);
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGImageRef newImage = nil;
if ( cameraDeviceSetting == CameraDeviceSetting640x480 )
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
newImage = CGBitmapContextCreateImage(newContext);
CGColorSpaceRelease( colorSpace );
CGContextRelease(newContext);
}
else
{
uint8_t *tempAddress = malloc( 640 * 4 * 480 );
memcpy( tempAddress, baseAddress, bytesPerRow * height );
baseAddress = tempAddress;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
newImage = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
newContext = CGBitmapContextCreate(baseAddress, 640, 480, 8, 640*4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextScaleCTM( newContext, (CGFloat)640/(CGFloat)width, (CGFloat)480/(CGFloat)height );
CGContextDrawImage(newContext, CGRectMake(0,0,640,480), newImage);
CGImageRelease(newImage);
newImage = CGBitmapContextCreateImage(newContext);
CGColorSpaceRelease( colorSpace );
CGContextRelease(newContext);
free( tempAddress );
}
if ( newImage != nil )
{
[self performSelectorOnMainThread:@selector(newCameraImageNotification:) withObject:(id)newImage waitUntilDone:YES];
}
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
}
static BOOL processingTouchEvent = NO;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ( processingTouchEvent )
return;
processingTouchEvent = YES;
[self snapShot];
[self swapCameras];
processingTouchEvent = NO;
}
@end
ScreenCapture.h
//
// ScreenCapture.h
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "GLView.h"
@interface ScreenCapture : NSObject
{
}
+ (UIImage *) UIViewToImage:(UIView *)view;
+ (UIImage *) GLViewToImage:(GLView *)glView;
@end
ScreenCapture.m
//
// ScreenCapture.m
// AugCam
//
// Created by John Carter on 1/26/2012.
//
#import "ScreenCapture.h"
#import <QuartzCore/CABase.h>
#import <QuartzCore/CATransform3D.h>
#import <QuartzCore/CALayer.h>
#import <QuartzCore/CAScrollLayer.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
@implementation ScreenCapture
+ (UIImage *) GLViewToImage:(GLView *)glView
{
UIImage *glImage = [GLView snapshot:glView]; // returns an autoreleased image
return glImage;
}
+ (UIImage *) UIViewToImage:(UIView *)view
{
// Create a graphics context with the target size
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
// camera image size extended to screen ratio so it captures the entire screen
//
CGSize imageSize = CGSizeMake( (CGFloat)480.0, (CGFloat)720.0 );
if (NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
else
UIGraphicsBeginImageContext(imageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
// Start with the view...
//
CGContextSaveGState(context);
CGContextTranslateCTM(context, [view center].x, [view center].y);
CGContextConcatCTM(context, [view transform]);
CGContextTranslateCTM(context,-[view bounds].size.width * [[view layer] anchorPoint].x,-[view bounds].size.height * [[view layer] anchorPoint].y);
[[view layer] renderInContext:context];
CGContextRestoreGState(context);
// ...then repeat for every subview from back to front
//
for (UIView *subView in [view subviews])
{
if ( [subView respondsToSelector:@selector(screen)] )
if ( [(UIWindow *)subView screen] == [UIScreen mainScreen] )
continue;
CGContextSaveGState(context);
CGContextTranslateCTM(context, [subView center].x, [subView center].y);
CGContextConcatCTM(context, [subView transform]);
CGContextTranslateCTM(context,-[subView bounds].size.width * [[subView layer] anchorPoint].x,-[subView bounds].size.height * [[subView layer] anchorPoint].y);
[[subView layer] renderInContext:context];
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // autoreleased image
UIGraphicsEndImageContext();
return image;
}
@end


Can you use this with ipad 2 and ios 5 ? My sis hasn’t upgraded to 5 yet and her program works and mine doesnt
The app has been tested and works with iOS 5 but I haven’t had a chance to test it on an iPad 2. It should work fine. Have her try this:
1. Double tap the home button. When you double tap the home button, the multitask view will appear.
2. Scroll to the right until you see the Live Effects Cam application icon.
3. Tap and hold the Live Effects Cam app icon, just as you would on the home screen to rearrange icons. All of the running application icons will begin to wiggle.
4. Tap on the red ⊖ button in the corner of the Live Effects Cam app icon. This will close and reset the app.
5. Double tap your home button again. When you double tap the home button this time, it will close your multitask view.
Then start the Live Effects Cam app up again to see if it’s working better now
Love “Live Effects Cam”! Terrific App!!!! Keep up the good work!
Thanks for a great app!
Out of all the “multi-filter/fx live cams” this has been the best so far for the iPhone 4/4s.
* 3 Reasons this is 5 stars *
1) Higher Resolution (960×1280)
2) Fast Process w/ Live Filters
3) No Watermark
There are other apps like:
* CamWow
* Orange Camera
* PowerCam
BUT “Live Effects Cam” beats them all in the 3 categories listed above.
Thanks for the great app!
Hi,
I am working on one app that contain CATransform3D on some image view. I want to take the screen shot of that view. But taking screen shot of transformed layer remove the transform effect from layer. I think you are also doing the same kind of thing in your app. Applying 3D transform on photo and capture the screen with OpenGL. Can you please help me to find out how it works ? I am new on OpenGL.
Great idea. I was actually considering taking advantage of the new CATransform3D ability as well. I’m not sure what you are doing to capture the screen. Take a look at the sample code above. There are two different screen capture methods. Then OPEN/GL won’t work for you if you are using a regular CALayer with transforms.