Friday, May 15, 2009

OpenGL ES for iPhone : Part 4 with More Drawings and OpenGL Screen Save

In some OpenGL 1.x books you might notice that the gl commands (like belows) are within the code block of glBegin() and glEnd() pair. These gl* commands must be converted to Vertices Array in order to be useful for OpenGL ES for iPhone.


glBegin(GL_LINE_STRIP);
z = -50.0f;
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f) { x = 50.0f*sin(angle); y = 50.0f*cos(angle); // Specify the point and move the z value up a little glVertex3f(x, y, z); z += 0.5f; } // Done drawing points glEnd();


Typically, you remove the glBegin() and glEnd() commands (that is immediate mode) and create the vertices array and implement the vertices position calculation (if any) inside the setupView and then remove other unsupported gl* commands before putting them to the iPhone OpenGL ES project code.

This is an example converting from the immediate mode to vertex arrays

immediate mode

glBegin(GL_TRIANGLES);
glVertex3f(-2.0, 0.5, 0.0);
glVertex3f(0.0, 4.0, -2.0);
glVertex3f(1.5, 2.5, -0.5);
glEnd();


vertex arrays

GLfloat vertices[] = { -2.0, 0.5, 0.0, 0.0, 4.0, -2.0, 1.5, 2.5, -0.5 };
glVertexPointer(3, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);


OpenGL ES does not support the full set of vertex array functions or parameters present in OpenGL






























FunctionNotes
glBegin()Not supported.
glEnd()Not supported.
glEdgeFlag[v]()Not supported.
glVertex{234}{sifd}[v]()Not supported.
glNormal3f()Supported.
glNormal3{bsifd}[v]()Not supported.
glNormal3{bsifd}[v]()Not supported.
glTexCoord{1234}{sifd}[v]()Not supported.
glMultiTexCoord4f()Supported.
glMultiTexCoord{1234}{sifd}[v]()Not supported.
glColor4f()Supported.
glColor{34}{bsifd ub us ui}[v]()Not supported.
glIndex{sifd ub}[v]()Not supported.
glVertexPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but support for
GL_BYTE has been added.
glNormalPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but support for
GL_BYTE has been added.
glColorPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but
support for GL_UNSIGNED_BYTE has been added.
In addition, the alpha value must be included with all colors;
there is no support for specifying only the RGB values.
glIndexPointer()Not supported.
glTexCoordPointer()Supported.
 Type cannot be GL_INT or GL_DOUBLE, but support
for GL_BYTE has been added. Also, because there is no support
for 1D textures, at least 2 texture coordinates must be provided
per vertex.
glEdgeFlagPointer()Not supported.
glInterleavedArrays()Not supported.
glArrayElement()Not supported.
glDrawArrays()GL_POINTS, GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES,
GL_TRIANGLE_STRIP, and GL_TRIANGLE_FAN are supported.
GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON are not supported.
glDrawElements()GL_POINTS, GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES,
GL_TRIANGLE_STRIP, and GL_TRIANGLE_FAN are supported.
GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON are not
supported. Type must either be GL_UNSIGNED_BYTE or
GL_UNSIGNED_SHORT (not GL_UNSIGNED_INT).
glDrawRangeElements()Supported.
glEnableClientState()Valid for all supported attributes.
glDisableClientState()Valid for all supported attributes.


Here are some of the typical drawings in 3D. This one is for a rotating Spiral


To use the source codes here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.m from below and paste them for Build & Go in XCode.

EAGLView.m (for Spiral) Select all

//
// EAGLView.m
// Spiral
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);

glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c = 0;
z = -50.0f;

// Loop around in a circle three times
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f) { // Calculate x and y values on the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bump up the z value z += 0.5f; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 0.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 0.0f, 0.0f, 0.0f); glRotatef(yRot, 1.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Set Line Width glLineWidth(3.0f); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; xRot += 0.1 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } - (void)layoutSubviews { [EAGLContext setCurrentContext:context]; [self destroyFramebuffer]; [self createFramebuffer]; [self drawView]; } - (BOOL)createFramebuffer { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [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 (USE_DEPTH_BUFFER) { 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; } } - (void)startAnimation { self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } - (void)stopAnimation { self.animationTimer = nil; } - (void)setAnimationTimer:(NSTimer *)newTimer { [animationTimer invalidate]; animationTimer = newTimer; } - (void)setAnimationInterval:(NSTimeInterval)interval { animationInterval = interval; if (animationTimer) { [self stopAnimation]; [self startAnimation]; } } - (void)dealloc { [self stopAnimation]; if ([EAGLContext currentContext] == context) { [EAGLContext setCurrentContext:nil]; } [context release]; [super dealloc]; } @end


And this one is for a rotating Fanned Circle.



EAGLView.m (Fanned Circle) Select all

//
// EAGLView.m
// Fanned Circle
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);


glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c;
z = 0.0f;
c = 0;
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f)) { // Top half of the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bottom half of the circle x = 50.0f*sin(angle+GL_PI); y = 50.0f*cos(angle+GL_PI); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 1.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Setup and render the points glEnable(GL_POINT_SMOOTH); glPointSize(1.0); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; zRot+=1.2 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } - (void)layoutSubviews { [EAGLContext setCurrentContext:context]; [self destroyFramebuffer]; [self createFramebuffer]; [self drawView]; } - (BOOL)createFramebuffer { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [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 (USE_DEPTH_BUFFER) { 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; } } - (void)startAnimation { self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } - (void)stopAnimation { self.animationTimer = nil; } - (void)setAnimationTimer:(NSTimer *)newTimer { [animationTimer invalidate]; animationTimer = newTimer; } - (void)setAnimationInterval:(NSTimeInterval)interval { animationInterval = interval; if (animationTimer) { [self stopAnimation]; [self startAnimation]; } } - (void)dealloc { [self stopAnimation]; if ([EAGLContext currentContext] == context) { [EAGLContext setCurrentContext:nil]; } [context release]; [super dealloc]; } @end


And also I have found a very nice method saveCurrentScreenToPhotoAlbum to capture the OpenGL view screen here. And below is an implementation on how to capture the screen in iPhone Simulator. You just touch/click the info button at the lower left bottom of iPhone Screen to trigger the screen capture to the Photo Album (Simulator or actual device).

To use the source codes here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.m from below and paste them for Build & Go in XCode. For this screenshot functionality, you need to add the CoreGraphics Framework to the Xcode Project before build & go.




EAGLView.m (Fanned Circle with Screen Capture) Select all

//
// EAGLView.m
// Fanned Circle with Screen Capture
//
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:self action:@selector(saveCurrentScreenToPhotoAlbum) forControlEvents:UIControlEventTouchUpInside];
infoButton.alpha = 0.5f;
infoButton.frame = CGRectMake(17, self.bounds.size.height-33, 33, 33);
[self addSubview:infoButton];
[self bringSubviewToFront:infoButton];

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);


glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c;
z = 0.0f;
c = 0;
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f)) { // Top half of the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bottom half of the circle x = 50.0f*sin(angle+GL_PI); y = 50.0f*cos(angle+GL_PI); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 1.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Setup and render the points glEnable(GL_POINT_SMOOTH); glPointSize(1.0); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; zRot+=0.1 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } // callback for CGDataProviderCreateWithData void releaseScreenshotData(void *info, const void *data, size_t size) { free((void *)data); }; // callback for UIImageWriteToSavedPhotosAlbum - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { NSLog(@"ScreenSave finished\n"); [image release]; // release image } - (void)saveCurrentScreenToPhotoAlbum { NSInteger myDataLength = backingWidth * backingHeight * 4; // allocate array and read pixels into it. GLuint *buffer = (GLuint *) malloc(myDataLength); glReadPixels(0, 0, backingWidth, backingHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer); // gl renders “upside down” so swap top to bottom into new array. for(int y = 0; y < x =" 0;" top =" buffer[y" bottom =" buffer[(backingHeight" provider =" CGDataProviderCreateWithData(NULL," bitspercomponent =" 8;" bitsperpixel =" 4" bytesperrow =" 4" colorspaceref =" CGColorSpaceCreateDeviceRGB();" bitmapinfo =" kCGBitmapByteOrderDefault;" renderingintent =" kCGRenderingIntentDefault;" imageref =" CGImageCreate(320," myimage =" [[UIImage" viewframebuffer =" 0;" viewrenderbuffer =" 0;" depthrenderbuffer =" 0;" animationtimer =" [NSTimer" animationtimer =" nil;" animationtimer =" newTimer;" animationinterval =" interval;">


In case you might also want to know how to do screenshot for a non-OpenGL ES content programatically. This is the code for viewcontroller. If you put the code in a UIView object change self.view to self

- (void) snapUIView
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil);
}





Wednesday, May 13, 2009

How to classdump SpringBoard header files and patch it

To classdump the SpringBoard header,

class-dump-x /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard -H -o Springboard

class-dump-x is available in Mac OS X and iPhone here

class-dumped header files are not directly usable and you should use this patch script to change the import statement of the dumped header files.

springboardheaderpatch.sh Select all

#/bin/sh
cd SpringBoard
perl -w -i -p -e "s/#import \"ISDownload.h\"/#import \<iTunesStore\/ISDownload.h\>/g" *.h
perl -w -i -p -e "s/#import \"ISNetworkMonitor-Protocol.h\"/#import \<iTunesStore\/ISNetworkMonitor-Protocol.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSArray.h\"/#import \<Foundation\/NSArray.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSCharacterSet.h\"/#import \<Foundation\/NSCharacterSet.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSDictionary.h\"/#import \<Foundation\/NSDictionary.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSMutableArray.h\"/#import \<Foundation\/NSMutableArray.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSObject.h\"/#import \<Foundation\/NSObject.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSObject-Protocol.h\"/#import \<Foundation\/NSObject-Protocol.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSOperation.h\"/#import \<Foundation\/NSOperation.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSString.h\"/#import \<Foundation\/NSString.h\>/g" *.h
perl -w -i -p -e "s/#import \"NSUserDefaults.h\"/#import \<Foundation\/NSUserDefaults.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBAlert.h\"/#import \<SpringBoard\/SBAlert.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBAlertDisplay.h\"/#import \<SpringBoard\/SBAlertDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBAlertInputView.h\"/#import \<SpringBoard\/SBAlertInputView.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBAlertItem.h\"/#import \<SpringBoard\/SBAlertItem.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBAlertWindow.h\"/#import \<SpringBoard\/SBAlertWindow.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBApplication.h\"/#import \<SpringBoard\/SBApplication.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBApplicationIcon.h\"/#import \<SpringBoard\/SBApplicationIcon.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBCallAlertDisplay.h\"/#import \<SpringBoard\/SBCallAlertDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBDismissOnlyAlertItem.h\"/#import \<SpringBoard\/SBDismissOnlyAlertItem.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBDisplay.h\"/#import \<SpringBoard\/SBDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBIcon.h\"/#import \<SpringBoard\/SBIcon.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBIconList.h\"/#import \<SpringBoard\/SBIconList.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBPhoneAlertItem.h\"/#import \<SpringBoard\/SBPhoneAlertItem.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBPlatformController.h\"/#import \<SpringBoard\/SBPlatformController.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBRingingAlertItem.h\"/#import \<SpringBoard\/SBRingingAlertItem.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSIMLockEntryAlertDisplay.h\"/#import \<SpringBoard\/SBSIMLockEntryAlertDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSIMToolkitAlert.h\"/#import \<SpringBoard\/SBSIMToolkitAlert.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSIMToolkitGetInputDisplay.h\"/#import \<SpringBoard\/SBSIMToolkitGetInputDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSIMToolkitTextAlertDisplay.h\"/#import \<SpringBoard\/SBSIMToolkitTextAlertDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSlidingAlertDisplay.h\"/#import \<SpringBoard\/SBSlidingAlertDisplay.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBSoundPreferences.h\"/#import \<SpringBoard\/SBSoundPreferences.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBStatusBarContentView.h\"/#import \<SpringBoard\/SBStatusBarContentView.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBStatusBarInCallView.h\"/#import \<SpringBoard\/SBStatusBarInCallView.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBTVOutController.h\"/#import \<SpringBoard\/SBTVOutController.h\>/g" *.h
perl -w -i -p -e "s/#import \"SBUSSDAlert.h\"/#import \<SpringBoard\/SBUSSDAlert.h\>/g" *.h
perl -w -i -p -e "s/#import \"SpringBoard.h\"/#import \<SpringBoard\/SpringBoard.h\>/g" *.h
perl -w -i -p -e "s/#import \"TPPhonePad.h\"/#import \<TelephonyUI\/TPPhonePad.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIAlertSheetTableCell.h\"/#import \<UIKit\/UIAlertSheetTableCell.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIApplication.h\"/#import \<UIKit\/UIApplication.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIControl.h\"/#import \<UIKit\/UIControl.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIImageView.h\"/#import \<UIKit\/UIImageView.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIModalView.h\"/#import \<UIKit\/UIModalView.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIModalViewDelegate-Protocol.h\"/#import \<UIKit\/UIModalViewDelegate-Protocol.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIPageControl.h\"/#import \<UIKit\/UIPageControl.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIPasscodeField.h\"/#import \<UIKit\/UIPasscodeField.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIPreferencesTableCell.h\"/#import \<UIKit\/UIPreferencesTableCell.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIPushButton.h\"/#import \<UIKit\/UIPushButton.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIScroller.h\"/#import \<UIKit\/UIScroller.h\>/g" *.h
perl -w -i -p -e "s/#import \"UITextField.h\"/#import \<UIKit\/UITextField.h\>/g" *.h
perl -w -i -p -e "s/#import \"UITextFieldDelegate-Protocol.h\"/#import \<UIKit\/UITextFieldDelegate-Protocol.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIView.h\"/#import \<UIKit\/UIView.h\>/g" *.h
perl -w -i -p -e "s/#import \"UIWindow.h\"/#import \<UIKit\/UIWindow.h\>/g" *.h



Here is another version that will dump and patch the 3.0 SDK SpringBoard headers using sed instead of perl

springboard30.sh Select all

#/bin/sh
SDKVER=3.0
rm -f pringBoard/* SpringBoard/*
class-dump-x /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard -H -o pringBoard
mkdir -p SpringBoard
for i in pringBoard/*.h
do
sed "s/\(#import \)\"\(NS.*\.h\)\"/\1\<Foundation\/\2\>/g;"\
"s/\(#import \)\"\(UI.*\.h\)\"/\1\<UIKit\/\2\>/g;"\
"s/\(#import \)\"\(IS.*\.h\)\"/\1\<iTunesStore\/\2\>/g;"\
"s/\(#import \)\"\(SB.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(TP.*\.h\)\"/\1\<TelephonyUI\/\2\>/g;"\
"s/\(#import \)\"\(SpringBoard.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(SPDaemon.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(VS.*\.h\)\"/\1\<VoiceServices\/\2\>/g;"\
"s/\(#import \)\"\(APS.*\.h\)\"/\1\<ApplePushService\/\2\>/g" $i > S$i
done
grep "import \".*h\"" SpringBoard/*.h



class dump and patch the 3.0 SDK UIKit headers using sed in iPhone

uikit30.sh Select all

#/bin/sh
class-dump /System/Library/Frameworks/UIKit.framework/UIKit -H -o IKit
mkdir -p UIKit
for i in IKit/*.h
do
sed "s/\(#import \)\"\(NS.*\.h\)\"/\1\<Foundation\/\2\>/g;"\
"s/\(#import \)\"\(UI.*\.h\)\"/\1\<UIKit\/\2\>/g;"\
"s/\(#import \)\"\(IS.*\.h\)\"/\1\<iTunesStore\/\2\>/g;"\
"s/\(#import \)\"\(SB.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(TP.*\.h\)\"/\1\<TelephonyUI\/\2\>/g;"\
"s/\(#import \)\"\(SpringBoard.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(SPDaemon.*\.h\)\"/\1\<SpringBoard\/\2\>/g;"\
"s/\(#import \)\"\(VS.*\.h\)\"/\1\<VoiceServices\/\2\>/g;"\
"s/\(#import \)\"\(APS.*\.h\)\"/\1\<ApplePushService\/\2\>/g;"\
"s/\(#import \)\"\(WebView.*\.h\)\"/\1\<WebKit\/\2\>/g;"\
"s/\(#import \)\"\(WebFrame.*\.h\)\"/\1\<WebKit\/\2\>/g;"\
"s/\(#import \)\"\(CA.*ayer.*\.h\)\"/\1\<QuartzCore\/\2\>/g;"\
"s/\(#import \)\"\(DOM.*\.h\)\"/\1\<WebKit\/\2\>/g" $i > U$i
done
grep "import \".*h\"" UIKit/*.h

Saturday, May 2, 2009

XCode GCC section missing in build settings

If you encounter this that the GCC 4 section was missing in XCode



All you need to do is to set the Active SDK to the same value as the Project Base SDK



Then you will see the GCC section again.

But it will disappear when you set it to Simulator again, this is a bug.