Wednesday, April 25, 2012

Create Bitmap Graphics Context on iPhone


The following function creates an UIImage object:
- (UIImage*)makeImage: (CGRect)rect
{
    CGFloat width = CGRectGetWidth(rect);
    CGFloat height = CGRectGetHeight(rect);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    size_t bitsPerComponent = 8;
    size_t bytesPerPixel    = 4;
    size_t bytesPerRow      = (width * bitsPerComponent * bytesPerPixel + 7) / 8;
    size_t dataSize         = bytesPerRow * height;
    
    unsigned char *data = malloc(dataSize);
    memset(data, 0, dataSize);

    CGContextRef context = CGBitmapContextCreate(data, width, height, 
                bitsPerComponent, 
                bytesPerRow, colorSpace, 
                kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    
    CGColorSpaceRelease(colorSpace);
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *result = [[UIImage imageWithCGImage:imageRef] retain];
    CGImageRelease(imageRef);
    CGContextRelease(context);
    free(data);    
    return result;
}

In this function I create a Bitmap Graphics Context:


CGContextRef context = CGBitmapContextCreate(...)


 and then the image:


CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *result = [[UIImage imageWithCGImage:imageRef] retain];


 The created graphics context works fine and, for a test, I can add a drawing code:

- (UIImage*)makeImage: (CGRect)rect
{
    CGFloat width = CGRectGetWidth(rect);
    CGFloat height = CGRectGetHeight(rect);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    size_t bitsPerComponent = 8;
    size_t bytesPerPixel    = 4;
    size_t bytesPerRow      = (width * bitsPerComponent * bytesPerPixel + 7) / 8;
    size_t dataSize         = bytesPerRow * height;
    
    unsigned char *data = malloc(dataSize);
    memset(data, 0, dataSize);

    CGContextRef context = CGBitmapContextCreate(data, width, height, 
                bitsPerComponent, 
                bytesPerRow, colorSpace, 
                kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    
    // a drawing for a test.
    CGContextSetRGBFillColor (context, 1, 0, 0, 1);
    CGContextFillRect (context, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (context, 0, 0, 1, .5);
    CGContextFillRect (context, CGRectMake (0, 0, 100, 200));
    
    CGColorSpaceRelease(colorSpace);
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *result = [[UIImage imageWithCGImage:imageRef] retain];
    CGImageRelease(imageRef);
    CGContextRelease(context);
    free(data);    
    return result;
}

This way allows a low-level access to the separate pixels of the image. For example:
for (int y = 20; y < 100; ++y)
    {
        for (int x = 20; x < 100; ++x)
        {
            int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
            data[byteIndex + 0] = 0;
            data[byteIndex + 1] = 127;
            data[byteIndex + 2] = 127;
            data[byteIndex + 3] = 127;
        }
    }

Probably, this code above contains a mistake. But I see a rectangle on the screen:

This method allows to draw on another image:

-(UIImage*)modify: (UIImage*)source
{
    CGImageRef sourceRef = source.CGImage;
    size_t width = CGImageGetWidth(sourceRef);
    size_t height = CGImageGetHeight(sourceRef);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    size_t bitsPerComponent = 8;
    size_t bytesPerPixel    = 4;
    size_t bytesPerRow      = (width * bitsPerComponent * bytesPerPixel + 7) / 8;
    size_t dataSize         = bytesPerRow * height;
    
    unsigned char *data = malloc(dataSize);
    memset(data, 0, dataSize);
    
    
    CGContextRef context = CGBitmapContextCreate(data, width, height, 
                  bitsPerComponent, bytesPerRow, colorSpace, 
                  kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), sourceRef);
    
    // a drawing for a test.
    CGContextSetRGBFillColor (context, 1, 0, 0, 1);
    CGContextFillRect (context, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (context, 0, 0, 1, .5);
    CGContextFillRect (context, CGRectMake (0, 0, 100, 200));
    
    // draw a rectange in a such strange way:
    for (int y = 120; y < 200; ++y)
    {
        for (int x = 20; x < 100; ++x)
        {
            int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
            data[byteIndex + 0] = 0;
            data[byteIndex + 1] = 200;
            data[byteIndex + 2] = 200;
            data[byteIndex + 3] = 200;
        }
    }
    
    CGColorSpaceRelease(colorSpace);
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *result = [[UIImage imageWithCGImage:imageRef] retain];
    CGImageRelease(imageRef);
    CGContextRelease(context);
    free(data);    
    return result;
}

Of course, without this function will look better, if we will not try to draw a rectangle in a strange way via the pixels. For example:
-(UIImage *)oneMore:(UIImage*)source
{
    CGSize size = [source size];
    UIGraphicsBeginImageContext(size);
    
    CGRect rect = CGRectMake(0.0f ,0.0f, size.width, size.height);
    [source drawInRect:rect];

    // a drawing for a test.
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBFillColor (context, 1, 0, 0, 1);
    CGContextFillRect (context, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (context, 0, 0, 1, .5);
    CGContextFillRect (context, CGRectMake (0, 0, 100, 200));
    
    //get image
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return (result);
}

I call this function in applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching: (UIApplication*)application
{
    CGRect rect = [[UIScreen mainScreen] bounds];
    UIWindow* window = [[UIWindow alloc] initWithFrame: rect];
    [window setBackgroundColor: [UIColor whiteColor]];
    
    rect = [[UIScreen mainScreen] applicationFrame];    
    //image = [self makeImage: rect];
    
    UIImage* source = [UIImage imageNamed:@"Abackground.png"];
    image = [self modify: source];
    
    UIImageView* contentView = [[UIImageView alloc] initWithFrame: rect];
    [contentView setImage:image];

    [window addSubview: contentView];
    [contentView release]; 
    
    [window makeKeyAndVisible];
}


Reference:
iOS Reference Library. Quartz 2D Programming Guide

No comments:

Post a Comment