2

I'm trying to set up a collision type hit test for a defined of pixels within a UIImageView. I'm only wish to cycle through pixels in a defined area.

Here's what I have so far:

- (BOOL)cgHitTestForArea:(CGRect)area {
    BOOL hit = FALSE;

    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();

    float areaFloat = ((area.size.width * 4) * area.size.height);
    unsigned char *bitmapData = malloc(areaFloat);    

    CGContextRef context = CGBitmapContextCreate(bitmapData,
                                                 area.size.width,
                                                 area.size.height,
                                                 8,
                                                 4*area.size.width,
                                                 colorspace,
                                                 kCGImageAlphaPremultipliedLast);
    CGContextTranslateCTM(context, -area.origin.x, -area.origin.y);
    [self.layer renderInContext:context];

    //Seek through all pixels.    
    float transparentPixels = 0;
    for (int i = 0; i < (int)areaFloat ; i += 4) {
        //Count each transparent pixel.
        if (((bitmapData[i + 3] * 1.0) / 255.0) == 0) {
            transparentPixels += 1;
        }
    }
    free(bitmapData);

    //Calculate the percentage of transparent pixels. 
    float hitTolerance = [[self.layer valueForKey:@"hitTolerance"]floatValue];

    NSLog(@"Apixels: %f hitPercent: %f",transparentPixels,(transparentPixels/areaFloat));

    if ((transparentPixels/(areaFloat/4)) < hitTolerance) {
        hit = TRUE;
    }    

    CGColorSpaceRelease(colorspace);
    CGContextRelease(context);

    return hit;    
}

Is someone able to offer any reason why it isn't working?

markturnip
  • 405
  • 2
  • 7
  • Nope. This wouldn't be very good performance for a game. – markturnip Aug 17 '11 at 10:41
  • Further to the above. It occasionally returns with a hit despite being in a fully transparent area. So I've tested with CGBitmapContextCreateImage to check which area is being iterated. But transparent pixels often appear corrupt. See image: http://imageshack.us/photo/my-images/109/screenshot201108311222.jpg/ – markturnip Aug 31 '11 at 11:49

1 Answers1

1

I would suggest using ANImageBitmapRep. It allows for easy pixel-level manipulation of images without the hassle of context, linking against other libraries, or raw memory allocation. To create an ANImgaeBitmapRep with the contents of a view, you could do something like this:

BMPoint sizePt = BMPointMake((int)self.frame.size.width, 
                             (int)self.frame.size.height);
ANImageBitmapRep * irep = [[ANImageBitmapRep alloc] initWithSize:sizePt];
CGContextRef ctx = [irep context];
[self.layer renderInContext:context];
[irep setNeedsUpdate:YES];

Then, you can crop out your desired rectangle. Note that coordinates are relative to the bottom left corner of the view:

// assuming aFrame is our frame
CGRect cFrame = CGRectMake(aFrame.origin.x,
                           self.frame.size.height - (aFrame.origin.y + aFrame.size.height),
                           aFrame.size.width, aFrame.size.height);

[irep cropFrame:];

Finally, you can find the percentage of alpha in the image using the following:

double totalAlpha;
double totalPixels;
for (int x = 0; x < [irep bitmapSize].x; x++) {
    for (int y = 0; y < [irep bitmapSize].y; y++) {
        totalAlpha += [irep getPixelAtPoint:BMPointMake(x, y)].alpha;
        totalPixels += 1;
    }
}
double alphaPct = totalAlpha / totalPixels;

You can then use the alphaPct variable as a percentage from 0 to 1. Note that, to prevent leaks, you must release the ANImageBitmapRep object using release: [irep release].

Hope that I helped. Image data is a fun and interesting field when it comes to iOS development.

Alex Nichol
  • 7,512
  • 4
  • 32
  • 30
  • Thanks Alex. This has given some good direction. You're comment about the coordinates relative to the bottom right doesn't seem to apply for me? Why would that be. – markturnip Aug 17 '11 at 10:56
  • In the code that I showed, I put something to translate the coordinates from the bottom left to the top left. The way CGContext works is that the bottom left is where the coordinates begin (when talking about transforms). – Alex Nichol Aug 17 '11 at 19:03
  • I've implementing something similar to your suggestion using ANImageBitmapRep. However, the pointInside delegate method for a UIView seems to be called three times on each touch. Is it inefficient to be creating & releasing ANImageBitmapRep 3 times per hit test? – markturnip Aug 30 '11 at 13:49
  • @markturnip yes, i would say it is inefficient. just make a static or instance variable, maybe – smdvlpr Sep 21 '11 at 20:46