415 lines
13 KiB
Objective-C
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
|
|
|