Files

415 lines
13 KiB
Objective-C

/*
* Copyright (c) 2006 Dave Dribin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#import "BasicOpenGLView.h"
static const int FULL_SCREEN_WIDTH = 640;
static const int FULL_SCREEN_HEIGHT = 480;
#define USE_CV_PIXEL_BUFFER 1
#define DOUBLE_BUFFERED 1
@interface BasicOpenGLView (Private)
- (CGImageRef) createImage;
- (void) loadTexture;
- (void) createTexture: (CGImageRef) image;
- (void) createTextureWithCoreVideo: (CGImageRef) image;
- (void) drawImage: (CGImageRef) image toBuffer: (void *) buffer
width: (size_t) width height: (size_t) height
bytesPerRow: (size_t) bytesPerRow;
@end
@implementation BasicOpenGLView
-(id) initWithFrame: (NSRect) frameRect
{
NSOpenGLPixelFormatAttribute colorSize = 32;
NSOpenGLPixelFormatAttribute depthSize = 32;
// pixel format attributes for the view based (non-fullscreen) NSOpenGLContext
NSOpenGLPixelFormatAttribute windowedAttributes[] =
{
// specifying "NoRecovery" gives us a context that cannot fall back to the software renderer
// this makes the view-based context a compatible with the fullscreen context,
// enabling us to use the "shareContext" feature to share textures, display lists, and other OpenGL objects between the two
NSOpenGLPFANoRecovery,
// attributes common to fullscreen and window modes
NSOpenGLPFAColorSize, colorSize,
NSOpenGLPFADepthSize, depthSize,
#if DOUBLE_BUFFERED
NSOpenGLPFADoubleBuffer,
#endif
NSOpenGLPFAAccelerated,
0
};
NSOpenGLPixelFormat * windowedPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: windowedAttributes];
[windowedPixelFormat autorelease];
self = [super initWithFrame: frameRect pixelFormat: windowedPixelFormat];
if (self == nil)
return nil;
[self setSyncToRefresh: YES];
// pixel format attributes for the full screen NSOpenGLContext
NSOpenGLPixelFormatAttribute fullScreenAttributes[] =
{
// specify that we want a fullscreen OpenGL context
NSOpenGLPFAFullScreen,
// we may be on a multi-display system (and each screen may be driven
// by a different renderer), so we need to specify which screen we want
// to take over.
// in this case, we'll specify the main screen.
NSOpenGLPFAScreenMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
// attributes common to fullscreen and window modes
NSOpenGLPFAColorSize, colorSize,
NSOpenGLPFADepthSize, depthSize,
#if DOUBLE_BUFFERED
NSOpenGLPFADoubleBuffer,
#endif
NSOpenGLPFAAccelerated,
0
};
NSOpenGLPixelFormat * fullScreenPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: fullScreenAttributes];
[fullScreenPixelFormat autorelease];
[self setFullScreenPixelFormat: fullScreenPixelFormat];
[self setFullScreenWidth: FULL_SCREEN_WIDTH height: FULL_SCREEN_HEIGHT];
mRect = NSMakeRect(0, 0, 160, 120);
mDirX = mDirY = 1;
mLastTime = 0.0f;
[self loadTexture];
return self;
}
- (void) dealloc
{
CVOpenGLTextureRelease(mTexture);
[super dealloc];
}
- (void) prepareOpenGL: (NSOpenGLContext *) context;
{
// init GL stuff here
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glPolygonOffset(1.0f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
- (void) update
{
[super update];
}
- (void) resize: (NSRect) bounds
{
glViewport(bounds.origin.x, bounds.origin.y, bounds.size.width,
bounds.size.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, bounds.size.width, 0, bounds.size.height, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
- (void) didEnterFullScreen;
{
NSLog(@"Enter full screen");
}
- (void) didExitFullScreen;
{
NSLog(@"Exit full screen");
}
- (void) updateAnimation;
{
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
if (mLastTime == 0.0f)
{
mRect.origin = NSMakePoint(0.0f, 0.0f);
mLastTime = currentTime;
return;
}
CFAbsoluteTime diff = currentTime - mLastTime;
mRect.origin.x += 250 * mDirX * diff;
mRect.origin.y += 300 * mDirY * diff;
if (mRect.origin.x < 0)
{
mDirX = 1;
mRect.origin.x = 0;
}
if (NSMaxX(mRect) > FULL_SCREEN_WIDTH)
{
mDirX = -1;
mRect.origin.x = FULL_SCREEN_WIDTH - mRect.size.width;
}
if (mRect.origin.y < 0)
{
mDirY = 1;
mRect.origin.y = 0;
}
if (NSMaxY(mRect) > FULL_SCREEN_HEIGHT)
{
mDirY = -1;
mRect.origin.y = FULL_SCREEN_HEIGHT - mRect.size.height;
}
mLastTime = currentTime;
return;
}
- (void) drawFrame
{
NSRect bounds = [self activeBounds];
[self resize: bounds];
// clear our drawable
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
NSRect rect;
float z;
z = 0.0f;
rect = NSMakeRect(0.0f, 0.0f, FULL_SCREEN_WIDTH - 1, FULL_SCREEN_HEIGHT - 1);
if ([self syncToRefresh])
glColor3f(1.0f, 0.0f, 0.0f);
else
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_LINES);
glVertex3f(rect.origin.x, rect.origin.y, z);
glVertex3f(NSMaxX(rect), rect.origin.y, z);
glVertex3f(NSMaxX(rect), rect.origin.y, z);
glVertex3f(NSMaxX(rect), NSMaxY(rect), z);
glVertex3f(NSMaxX(rect), NSMaxY(rect), z);
glVertex3f(rect.origin.x, NSMaxY(rect), z);
glVertex3f(rect.origin.x, NSMaxY(rect), z);
glVertex3f(rect.origin.x, rect.origin.y, z);
glEnd();
GLfloat vertices[4][2];
GLfloat texCoords[4][2];
glColor3f(1.0f, 1.0f, 1.0f);
// Configure OpenGL to get vertex and texture coordinates from our two arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
rect = mRect;
// Top left
vertices[0][0] = rect.origin.x;
vertices[0][1] = rect.origin.y;
// Bottom left
vertices[1][0] = NSMaxX(rect);
vertices[1][1] = rect.origin.y;
// Bottom right
vertices[2][0] = NSMaxX(rect);
vertices[2][1] = NSMaxY(rect);
// Top right
vertices[3][0] = rect.origin.x;
vertices[3][1] = NSMaxY(rect);
// Get the current texture's coordinates, bind the texture,and draw our
// rectangle
rect.origin = NSMakePoint(0, 0);
texCoords[0][0] = rect.origin.x;
texCoords[0][1] = rect.origin.y;
texCoords[1][0] = NSMaxX(rect);
texCoords[1][1] = rect.origin.y;
texCoords[2][0] = NSMaxX(rect);
texCoords[2][1] = NSMaxY(rect);
texCoords[3][0] = rect.origin.x;
texCoords[3][1] = NSMaxY(rect);
glEnable(mTextureTarget);
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(mTextureTarget, mTextureName);
glDrawArrays(GL_QUADS, 0, 4);
glDisable(mTextureTarget);
}
@end
@implementation BasicOpenGLView (Private)
- (CGImageRef) createImage;
{
NSBundle * myBundle = [NSBundle bundleForClass: [self class]];
NSString * path = [myBundle pathForImageResource: @"image"];
NSURL * url = [NSURL fileURLWithPath: path];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef) url, nil);
CGImageRef image = CGImageSourceCreateImageAtIndex (imageSource, 0, nil);
CFRelease(imageSource);
return image;
}
- (void) loadTexture;
{
mTexture = NULL;
CGImageRef image = [self createImage];
if (!USE_CV_PIXEL_BUFFER)
{
NSLog(@"Create texture directly");
[self createTexture: image];
}
else
{
NSLog(@"Create texture with Core Video");
[self createTextureWithCoreVideo: image];
}
CFRelease(image);
}
- (void) createTexture: (CGImageRef) image;
{
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
void * pixelBuffer = calloc(width*4, height);
[self drawImage: image toBuffer: pixelBuffer
width: width height: height
bytesPerRow: width*4];
[[self openGLContext] makeCurrentContext];
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &mTextureName);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mTextureName);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#if __BIG_ENDIAN__
GLenum type = GL_UNSIGNED_INT_8_8_8_8_REV;
#else
GLenum type = GL_UNSIGNED_INT_8_8_8_8;
#endif
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, width, height,
0, GL_BGRA_EXT, type, pixelBuffer);
free(pixelBuffer);
mTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
mRect.size.width = width;
mRect.size.height = height;
}
- (void) createTextureWithCoreVideo: (CGImageRef) image;
{
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
CVReturn rc;
CVPixelBufferRef pixelBuffer;
rc = CVPixelBufferCreate(NULL,
width,
height,
k32ARGBPixelFormat,
NULL, // pixelBufferAttributes
&pixelBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
void * baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
[self drawImage: image toBuffer: baseAddress
width: width height: height
bytesPerRow: bytesPerRow];
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
// It is rather wasteful to create the cache, and delete it right away,
// but the only way to create a CVOpenGLTexture is with a cache.
CVOpenGLTextureCacheRef textureCache;
rc = CVOpenGLTextureCacheCreate(NULL, NULL,
[[self openGLContext] CGLContextObj],
[[self pixelFormat] CGLPixelFormatObj],
NULL,
&textureCache);
rc = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
textureCache,
pixelBuffer,
NULL,
&mTexture);
CVOpenGLTextureCacheRelease(textureCache);
CVPixelBufferRelease(pixelBuffer);
mTextureName = CVOpenGLTextureGetName(mTexture);
mTextureTarget = CVOpenGLTextureGetTarget(mTexture);
mRect.size.width = width;
mRect.size.height = height;
}
- (void) drawImage: (CGImageRef) image toBuffer: (void *) buffer
width: (size_t) width height: (size_t) height
bytesPerRow: (size_t) bytesPerRow;
{
CGRect rect = {{0, 0}, {width, height}};
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(buffer,
width, height, 8,
bytesPerRow, space,
kCGImageAlphaPremultipliedFirst);
// Flip the axis, as per:
// http://developer.apple.com/qa/qa2001/qa1009.html
// Move the CG origin to the upper left of the port
CGContextTranslateCTM(bitmapContext, 0,
(float)(height));
// Flip the y axis so that positive Y points down
CGContextScaleCTM(bitmapContext, 1.0, -1.0);
CGContextDrawImage(bitmapContext, rect, image);
CGContextRelease(bitmapContext);
}
@end