O'Reilly logo

iPhone 3D Programming by Philip Rideout

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Generating and Transforming OpenGL Textures with Quartz

You can use Quartz to draw 2D paths into an OpenGL texture, resize the source image, convert from one format to another, and even generate text. We’ll cover some of these techniques in Chapter 7; for now let’s go over a few simple ways to generate textures.

One way of loading textures into OpenGL is creating a Quartz surface using whatever format you’d like and then drawing the source image to it, as shown in Example 5-22.

Example 5-22. Texture loading with CGContextDrawImage

TextureDescription LoadImage(const string& file)
{
    NSString* basePath = [NSString stringWithUTF8String:file.c_str()];
    NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
    NSString* fullPath = 
      [resourcePath stringByAppendingPathComponent:basePath];
    UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath];1
            
    TextureDescription description;
    description.Size.x = CGImageGetWidth(uiImage.CGImage);
    description.Size.y = CGImageGetHeight(uiImage.CGImage);
    description.BitsPerComponent = 8;
    description.Format = TextureFormatRgba;
    description.MipCount = 1;
    m_hasPvrHeader = false;

    int bpp = description.BitsPerComponent / 2;2
    int byteCount = description.Size.x * description.Size.y * bpp;
    unsigned char* data = (unsigned char*) calloc(byteCount, 1);3
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = 
      kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
    CGContextRef context = CGBitmapContextCreate(data,4
        description.Size.x,
        description.Size.y,
        description.BitsPerComponent,
        bpp * description.Size.x,
        colorSpace,
        bitmapInfo);
    CGColorSpaceRelease(colorSpace);
    CGRect rect = CGRectMake(0, 0, description.Size.x, description.Size.y);
    CGContextDrawImage(context, rect, uiImage.CGImage);5
    CGContextRelease(context);
    
    m_imageData = [NSData dataWithBytesNoCopy:data 
                                       length:byteCount 
                                 freeWhenDone:YES];6
    return description;
}
1

As before, use the imageWithContentsOfFile class method to create and allocate a UIImage object that wraps a CGImage object.

2

Since there are four components per pixel (RGBA), the number of bytes per pixel is half the number of bits per component.

3

Allocate memory for the image surface, and clear it to zeros.

4

Create a Quartz context with the memory that was just allocated.

5

Use Quartz to copy the source image onto the destination surface.

6

Create an NSData object that wraps the memory that was allocated.

If you want to try the Quartz-loading code in the sample app, perform the following steps:

  1. Add Example 5-22 to ResourceManager.mm.

  2. Add the following method declaration to IResourceManager in Interfaces.hpp:

    virtual TextureDescription LoadImage(const string& file) = 0;
  3. In the SetPngTexture method in RenderingEngine.TexturedES2.cpp, change the LoadPngImage call to LoadImage.

  4. In your render engine’s Initialize method, make sure your minification filter is GL_LINEAR and that you’re calling SetPngTexture.

One advantage of loading images with Quartz is that you can have it do some transformations before uploading the image to OpenGL. For example, say you want to flip the image vertically. You could do so by simply adding the following two lines immediately before the line that calls CGContextDrawImage:

CGContextTranslateCTM(context, 0, description.Size.y);
CGContextScaleCTM(context, 1, -1);

Another neat thing you can do with Quartz is generate new images from scratch in real time. This can shrink your application, making it faster to download. This is particularly important if you’re trying to trim down to less than 10MB, the maximum size that Apple allows for downloading over the 3G network. Of course, you can do this only for textures that contain simple vector-based images, as opposed to truly artistic content.

For example, you could use Quartz to generate a 256×256 texture that contains a blue filled-in circle, as in Example 5-23. The code for creating the surface should look familiar; lines of interest are shown in bold.

Example 5-23. ResourceManager::GenerateCircle()

TextureDescription GenerateCircle()
{
    TextureDescription description;
    description.Size = ivec2(256, 256);
    description.BitsPerComponent = 8;
    description.Format = TextureFormatRgba;

    int bpp = description.BitsPerComponent / 2;
    int byteCount = description.Size.x * description.Size.y * bpp;
    unsigned char* data = (unsigned char*) calloc(byteCount, 1);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
    CGContextRef context = CGBitmapContextCreate(data,
                                                 description.Size.x,
                                                 description.Size.y,
                                                 description.BitsPerComponent,
                                                 bpp * description.Size.x,
                                                 colorSpace,
                                                 bitmapInfo);
    CGColorSpaceRelease(colorSpace);
            
    CGRect rect = CGRectMake(5, 5, 246, 246);
    CGContextSetRGBFillColor(context, 0, 0, 1, 1);
    CGContextFillEllipseInRect(context, rect);

    CGContextRelease(context);
    
    m_imageData = [NSData dataWithBytesNoCopy:data length:byteCount freeWhenDone:YES];
    return description;
}

If you want to try the circle-generation code in the sample app, perform the following steps:

  1. Add Example 5-23 to ResourceManager.mm.

  2. Add the following method declaration to IResourceManager in Interfaces.hpp:

    virtual TextureDescription GenerateCircle() = 0;
  3. In the SetPngTexture method in RenderingEngine.TexturedES2.cpp, change the LoadImage call to GenerateCircle.

  4. In your render engine’s Initialize method, make sure your minification filter is GL_LINEAR and that you’re calling SetPngTexture.

Quartz is a rich 2D graphics API and could have a book all to itself, so I can’t cover it here; check out Apple’s online documentation for more information.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required