Sources/GLTextureDI.mm
/* |
Copyright (C) 2015 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Utility class for generating a GL texture from an image. |
*/ |
#import <OpenGL/gl3.h> |
#import <OpenGL/glext.h> |
#import "NSBitmap.h" |
#import "GLTextureDI.h" |
@implementation GLTextureDI |
{ |
@private |
BOOL isFinalized; |
BOOL _bind; |
BOOL _enable; |
GLuint _texture; |
GLint _internal; |
GLenum _target; |
GLenum _format; |
GLenum _type; |
GLsizei _width; |
GLsizei _height; |
size_t _samplesPerPixel; |
size_t _bitsPerComponent; |
} |
- (BOOL) _newTexture:(nonnull IOSurface2D *)surface |
context:(nonnull CGLContextObj)context |
{ |
BOOL success = NO; |
glGenTextures(1, &_texture); |
glEnable(_target); |
{ |
glBindTexture(_target, _texture); |
{ |
glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
glTexParameteri(_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
glTexParameteri(_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
CGLError error = CGLTexImageIOSurface2D(context, |
_target, |
_internal, |
_width, |
_height, |
_format, |
_type, |
surface.surface, |
0); |
success = error == kCGLNoError; |
if(!success) |
{ |
// TODO: Remove the fallback scheme when CGLTexImageIOSurface2D(...) full supports all deep image pixel combinations |
NSLog(@">> MESSAGE[%d]: Fall back scheme for creating a 2d texture from an i/o surface!", error); |
// Lock the input i/o surface |
success = [surface lock:YES]; |
// NOTE: This path works with GL_TEXTURE_RECTANGLE target, even though it isn't |
// as performant as the live update path. |
if(success) |
{ |
glTexImage2D(_target, |
0, |
_internal, |
_width, |
_height, |
0, |
_format, |
_type, |
[surface map]); |
[surface unlock]; |
} // if |
} // if |
} |
glBindTexture(_target, 0); |
} |
glDisable(_target); |
return success; |
} // _newTexture |
- (BOOL) _newTexture:(nullable const GLvoid *)pixels |
{ |
glGenTextures(1, &_texture); |
glEnable(_target); |
{ |
glBindTexture(_target, _texture); |
{ |
glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
glTexParameteri(_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
glTexParameteri(_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
glTexImage2D(_target, |
0, |
_internal, |
_width, |
_height, |
0, |
_format, |
_type, |
pixels); |
} |
glBindTexture(_target, 0); |
} |
glDisable(_target); |
return _texture != 0; |
} // _newTexture |
- (void) _initProperties |
{ |
if(_bitsPerComponent == 16) |
{ |
_internal = (_samplesPerPixel == 4) ? GL_RGBA16 : GL_RGB16; |
_format = (_samplesPerPixel == 4) ? GL_RGBA : GL_RGB; |
_type = GL_UNSIGNED_SHORT; |
} // if |
else |
{ |
// NOTE: For 32-bit images and live update to an i/o surface using |
// CGLTexImageIOSurface2D(...), the correct format should be |
// GL_BGRA or GL_BGR. However, this combination fails to |
// create a correct texture, and the live update using the |
// CGLTexImageIOSurface2D(...) returns no error. But, to |
// display the 32-bit texture, we deliberately select a |
// GL_RGBA or GL_RGB to force CGLTexImageIOSurface2D(...) |
// api to return an error so that the fallback scheme can |
// generate the texture for us. |
_internal = (_samplesPerPixel == 4) ? GL_RGBA8 : GL_RGB8; |
_format = (_samplesPerPixel == 4) ? GL_RGBA : GL_RGB; |
_type = GL_UNSIGNED_INT_8_8_8_8_REV; |
} // else |
} // _initProperties |
- (BOOL) _initTextureWithSurface:(nonnull IOSurface2D *)surface |
context:(nonnull CGLContextObj)context |
{ |
BOOL success = NO; |
if(surface && context) |
{ |
_samplesPerPixel = surface.samplesPerPixel; |
if((_samplesPerPixel == 3) || (_samplesPerPixel == 4)) |
{ |
_bitsPerComponent = surface.bitsPerComponent; |
_target = GL_TEXTURE_RECTANGLE; |
_width = GLsizei(surface.width); |
_height = GLsizei(surface.height); |
[self _initProperties]; |
success = [self _newTexture:surface |
context:context]; |
} // if |
} // if |
return success; |
} // _initTextureWithImage |
- (BOOL) _initTextureWithSurface:(nonnull IOSurface2D *)surface |
{ |
CGLContextObj context = CGLGetCurrentContext(); |
if(context != nullptr) |
{ |
return [self _initTextureWithSurface:surface |
context:context]; |
} // if |
return NO; |
} // _initTextureWithSurface |
- (BOOL) _initTextureWithImage:(nullable NSImage *)image |
{ |
BOOL success = NO; |
if(image) |
{ |
NSBitmapImageRep* bitmap = [NSBitmap bitmapWithImage:image].bitmap; |
if(bitmap) |
{ |
_samplesPerPixel = bitmap.samplesPerPixel; |
if(!bitmap.isPlanar && ((_samplesPerPixel == 3) || (_samplesPerPixel == 4))) |
{ |
_bitsPerComponent = bitmap.bitsPerSample; |
_target = GL_TEXTURE_2D; |
_width = GLsizei(bitmap.pixelsWide); |
_height = GLsizei(bitmap.pixelsHigh); |
[self _initProperties]; |
success = [self _newTexture:bitmap.bitmapData]; |
} // if |
} // if |
} // if |
return success; |
} // _initTextureWithImage |
- (BOOL) _initTextureWithFile:(nullable NSString *)path |
{ |
BOOL success = NO; |
if(path) |
{ |
// load the source image and pass it to our windows/views - |
NSImage* image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease]; |
success = [self _initTextureWithImage:image]; |
} // if |
return success; |
} // _initTextureWithFile |
- (BOOL) _initTextureWithURL:(nullable NSURL *)url |
{ |
BOOL success = NO; |
if(url) |
{ |
// load the source image and pass it to our windows/views - |
NSImage* image = [[[NSImage alloc] initWithContentsOfURL:url] autorelease]; |
success = [self _initTextureWithImage:image]; |
} // if |
return success; |
} // _initTextureWithFile |
- (BOOL) _initTextureWithResource:(nullable NSString *)name |
{ |
BOOL success = NO; |
if(name) |
{ |
// Acquire the absolute pathname to the imge resource in application's bundle |
NSString* resource = [[NSBundle mainBundle] resourcePath]; |
NSString* path = [NSString stringWithFormat:@"%@/%@", resource, name]; |
success = [self _initTextureWithFile:path]; |
} // if |
return success; |
} // initTextureWithResource |
- (BOOL) _initTexture |
{ |
_samplesPerPixel = 4; |
_bitsPerComponent = 16; |
_texture = 0; |
_target = GL_TEXTURE_2D; |
_width = 1920; |
_height = 1080; |
[self _initProperties]; |
return NO; |
} // _initTexture |
// Default initializer, sets the properties for representation |
// of 1920x1080 64-bit RGBA texture 2D deep image |
- (nullable instancetype) init |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTexture]; |
} // if |
return self; |
} // init |
// Create a texture from a 2D I/O surface using the current CGL context |
- (nullable instancetype) initWithSurface:(nonnull IOSurface2D *)surface |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithSurface:surface]; |
} // if |
return self; |
} // initWithSurface |
// Create a texture from a 2D I/O surface using the CGL context object |
- (nullable instancetype) initWithSurface:(nonnull IOSurface2D *)surface |
context:(nonnull CGLContextObj)context |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithSurface:surface |
context:context]; |
} // if |
return self; |
} // initWithSurface |
// Create a texture from an image |
- (nullable instancetype) initWithImage:(nullable NSImage *)image |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithImage:image]; |
} // if |
return self; |
} // initWithImage |
// Create a texture from an image file located at an absolute path |
- (nullable instancetype) initWithFile:(nullable NSString *)path |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithFile:path]; |
} // if |
return self; |
} // initWithFile |
// Create a texture from an image file located at a URL |
- (nullable instancetype) initWithURL:(nullable NSURL *)url |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithURL:url]; |
} // if |
return self; |
} // initWithURL |
// Create a texture from an image file in application's bundle |
- (nullable instancetype) initWithResource:(nullable NSString *)name |
{ |
self = [super init]; |
if(self) |
{ |
isFinalized = [self _initTextureWithResource:name]; |
} // if |
return self; |
} // initWithResource |
// Destructor |
- (void) dealloc |
{ |
if(_texture) |
{ |
glDeleteTextures(1, &_texture); |
} // if |
[super dealloc]; |
} // dealloc |
// Set teture's target type if the texture isn't already |
// created by one of the designated initializers |
- (void) setTarget:(GLenum)target |
{ |
if(!isFinalized) |
{ |
_target = ((target == GL_TEXTURE_2D) || (target == GL_TEXTURE_RECTANGLE)) ? target : GL_TEXTURE_2D; |
} // if |
} // target |
// Set samples-per-pixel if the texture is not already |
// created by one of the designated initializers |
- (void) setSamplesPerPixel:(size_t)samplesPerPixel |
{ |
if(!isFinalized) |
{ |
_samplesPerPixel = ((samplesPerPixel == 3) || (samplesPerPixel == 4)) ? samplesPerPixel : 4; |
} // if |
} // setSamplesPerPixel |
// Set bits-per-component if the texture is not already |
// created by one of the designated initializers |
- (void) setBitsPerComponent:(size_t)bitsPerComponent |
{ |
if(!isFinalized) |
{ |
_bitsPerComponent = ((bitsPerComponent == 8) || (bitsPerComponent == 16)) ? bitsPerComponent : 16; |
} // if |
} // setBitsPerComponent |
// Set texture's width if the texture is not already |
// created by one of the designated initializers |
- (void) setWidth:(GLsizei)width |
{ |
if(!isFinalized) |
{ |
_width = (width != 0) ? width : 1920; |
} // if |
} // setWidth |
// Set texture's height if the texture is not already |
// created by one of the designated initializers |
- (void) setHeight:(GLsizei)height |
{ |
if(!isFinalized) |
{ |
_height = (height != 0) ? height : 1080; |
} // if |
} // setHeight |
// Create a new texture if the properties were set and default |
// initializer was used to instantiate the object. |
- (BOOL) acquire:(nullable const void *)pixels |
{ |
if(!isFinalized) |
{ |
[self _initProperties]; |
isFinalized = [self _newTexture:pixels]; |
} // if |
return isFinalized; |
} // acquire |
- (BOOL) acquire |
{ |
return [self acquire:nullptr]; |
} // acquire |
// Update the texture |
- (BOOL) update:(nonnull const void *)pixels |
{ |
BOOL success = isFinalized && (pixels != nullptr); |
if(success) |
{ |
glTexSubImage2D(_target, 0, 0, 0, _width, _height, _format, _type, pixels); |
} // if |
return success; |
} // pixels |
// Update the texture starting at an offset |
- (BOOL) update:(nonnull const void *)pixels |
point:(const CGPoint)offset |
{ |
BOOL success = isFinalized && (pixels != nullptr); |
if(success) |
{ |
GLint xoffset = GLint(offset.x); |
GLint yoffset = GLint(offset.y); |
glTexSubImage2D(_target, 0, xoffset, yoffset, _width, _height, _format, _type, pixels); |
} // if |
return success; |
} // pixels |
// Bind/unbind texture target |
- (void) setBind:(BOOL)bind |
{ |
_bind = bind; |
if(_bind) |
{ |
glBindTexture(_target, _texture); |
} // if |
else |
{ |
glBindTexture(_target, 0); |
} // else |
} // setBind |
// Enable/disable texture target |
- (void) setEnable:(BOOL)enable |
{ |
_enable = enable; |
if(_enable) |
{ |
glEnable(_target); |
} // if |
else |
{ |
glDisable(_target); |
} // else |
} // setEnable |
@end |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-12-10