views:

32

answers:

1

I would like to create a masking effect over a UIView in order to accomplish the following. I will display a sealed box in the screen and the user will be able to touch (scratch) the screen in order to reveal whats behind that image(UIView). Something similar to those lottery tickets where u r suppose to scratch some cover material thats on top of the results..

If someone could point me in the right direction would be awesome, I'm not sure how to start doing this...

thanks

+1  A: 

Pixel editing is usually done with a CGBitmapContext. In this case, I think you will want to create a grayscale bitmap that represents just the alpha channel of the scratch area. As the user scratches, you will paint in this bitmap.

To use a GCBitmapContext as the mask for an image, you must first create a masking image from the bitmap. Use CGImageMaskCreate and pass in a data provider that points to the same pixels used to create the bitmap. Then use CGImageCreateWithMask with your scratch off image and the mask image that is the bitmap.

You cannot draw directly in the iPhone. Every time the user moves a finger, you will have to modify the mask bitmap then invalidate the UIView that draws the image. You may just be able to draw the same image again, or you may need to reconstruct the mask and masked image each time you draw. As long as the mask image refers directly to the bitmap pixel data, very little memory is actually allocated.

So in psuedocode you want something like this:

scratchableImage = ...
width = CGImageGetWidth( scratchableImage );
height = CGImageGetHeight( scratchableImage );
colorspace = CGColorSpaceCreateDeviceGray();
pixels = CFDataCreateMutable( NULL , width * height );
bitmap = CFBitmapContextCreate( CFDataGetMutableBytePtr( pixels ) , width , height , 8 , width , colorspace , kCGImageAlphaNone );
provider = CGDataProviderCreateWithCFData( pixels );
mask = CGImageMaskCreate( width , height , 8 , 8 , width , provider , NULL , false , kCGRenderingIntentDefault );
scratched = CGImageCreateWithImageInRect( scratchableImage , mask );

At this point, scratched will have an alpha channel dictated by bitmap but bitmap has garbage data. White pixels in bitmap are opaque, and black pixels are clear. Paint all white pixels in bitmap then, as the user scratches, paint black pixels. I think that changes to bitmap will automatically apply every time scratched is drawn, but if not then just recreate mask and scratched every time you draw.

You probably have a custom UIView for tracking user input. You could derive your custom view from UIImageView and let it draw the image or do the following:

-(void) drawRect:(CGRect)inDirty {
  // assume scratched is a member
  CGContextDrawImage( UIGraphicsGetCurrentContext() , [self bounds] , scratched );
}

Alternately you can skip creating scratched and instead use CGContextClipToMask and draw the original scratchableImage directly. If there is no scratchableImage and your scratch area is a texture or view hierarchy, take this approach.

drawnonward