ADC Home > Reference Library > Technical Q&As > Graphics & Imaging > OpenGL >

Creating an OpenGL texture from an NSView


Q: How do I create an OpenGL texture from an NSView?

A: Virtually any NSView or subclass thereof can be used as a texture in OpenGL. The basic process involves using an NSBitmapImageRep to store the NSView's image data in a format that can be readily used as texture data by OpenGL. The following steps more specifically define this methodology:

  • Allocate an NSBitmapImageRep to use for storing the texture data.
  • Initialize the NSBitmapImageRep using the -initWithFocusedViewRect: method and the NSView -bounds method.
  • Use the NSBitmapImageRep method -bitmapData to retrieve the actual pixel data and pass this to OpenGL as a texture with the appropriate parameters.

Listing 1 demonstrates a self-contained Cocoa method showing this technique in practice.

Listing 1. Building an OpenGL texture from an NSView

// Generate texture 'texName' from 'theView' in current OpenGL context
-(void)textureFromView:(NSView*)theView textureName:(GLuint*)texName
{
    // Bitmap generation from source view
    NSBitmapImageRep * bitmap = [NSBitmapImageRep alloc];
    int samplesPerPixel = 0;
    
    [theView lockFocus];
    [bitmap initWithFocusedViewRect:[theView bounds]];
    [theView unlockFocus];

    // Set proper unpacking row length for bitmap
    glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]);

    // Set byte aligned unpacking (needed for 3 byte per pixel bitmaps)
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    // Generate new texture object if none passed in
    if (*texName == 0)
         glGenTextures (1, texName);
     glBindTexture (GL_TEXTURE_RECTANGLE_EXT, *texName);
    // Non-mipmap filtering (redundant for texture_rectangle)
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 
                    GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    samplesPerPixel = [bitmap samplesPerPixel];

    // Non-planar, RGB 24 bit bitmap, or RGBA 32 bit bitmap
    if(![bitmap isPlanar] && 
       (samplesPerPixel == 3 || samplesPerPixel == 4)) { 
         glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 
                      0, 
                      samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
                      [bitmap pixelsWide], 
                      [bitmap pixelsHigh], 
                      0, 
                      samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
                      GL_UNSIGNED_BYTE, 
                      [bitmap bitmapData]);
    } else {
    /*
        Error condition...
        The above code handles 2 cases (24 bit RGB and 32 bit RGBA),
        it is possible to support other bitmap formats if desired.
        
        So we'll log out some useful information.
    */
        NSLog (@"-textureFromView: Unsupported bitmap data
        format: isPlanar:%d, samplesPerPixel:%d, bitsPerPixel:%d,
        bytesPerRow:%d, bytesPerPlane:%d",
            [bitmap isPlanar], 
            [bitmap samplesPerPixel], 
            [bitmap bitsPerPixel], 
            [bitmap bytesPerRow], 
            [bitmap bytesPerPlane]);
    }
    // Clean up
    [bitmap release];
}

The following are some important notes to keep in mind when reviewing the above code:

  • GL_EXT_texture_rectangle is used for non-power of two texture support, which is not supported on the Rage128.
  • gluScaleImage() can be used to scale non-PoT textures into PoT dimensions for hardware that doesn't support GL_EXT_texture_rectangle.
  • A valid current OpenGL context is required.
  • There are certain views that do not work with this type of operation. At the present time, those views are NSProgressIndicator, NSMovieView and NSOpenGLView. initWithFocusedViewRect: reads from the window backing store which these particular views do not use. These views use a separate surface for drawing that is not read by initWithFocusedViewRect:.

[Feb 13, 2004]


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.