1

I am developing an iPad application that has multiple UIImageViews in a viewcontoller. each image has some transparent parts. when the user click on the image I want to test if the area of the image he clicked on was NOT transparent then I want to do some action

after searching I have reached the conclusion that I have to access the rawdata of the image and check the alpha value of the point the user clicked on

I used the solution found in here and it helped a lot. I modified the code so that if the point the user clicked on was transparent (alpha <1) then prent 0 else print 1. however, the result is not accurate in runtime. I sometimes get 0 where the point clicked is not transparent and viseversa. I think that there is a problem with the byteIndex value I am not sure that it returnes the color data at the point the user clicked

here is my code

   CGPoint touchPoint;
    - (void)viewDidLoad
{
    [super viewDidLoad];
    [logo addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]];
    }

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [[event allTouches] anyObject];
     touchPoint = [touch locationInView:self.view];

}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{

    int x = touchPoint.x;
    int y = touchPoint.y;

    [self getRGBAsFromImage:img atX:x andY:y];
}

- (void)getRGBAsFromImage:(UIImage*)image atX:(int)xx andY:(int)yy {

    // First get the image into your data buffer
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;


        CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0;
        byteIndex += 4;
        if (alpha < 1) {
            NSLog(@"0");
// here I should add the action I want

        }
        else NSLog(@"1");



    free(rawData);

}

Thank you in advace

Community
  • 1
  • 1
Alsmayer
  • 236
  • 1
  • 4
  • 13

1 Answers1

2

That method is bad as it draws the full image every time and also relies on the exact byte layout.

I propose to only draw into a 1x1 context and get alpha of that


- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
int x = touchPoint.x;
int y = touchPoint.y;

CGFloat alpha = [self getAlphaFromImage:img atX:x andY:y];
if(alpha<1)
   …
}

- (CGFloat)getAlphaFromImage:(UIImage*)image atX:(NSInteger)xx andY:(NSInteger)yy {
// Cancel if point is outside image coordinates
if (!CGRectContainsPoint(CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), CGPointMake(xx,yy))) {
    return 0;
}

// Create a 1x1 pixel byte array and bitmap context to draw the pixel into.
// Reference: http://stackoverflow.com/questions/1042830/retrieving-a-pixel-alpha-value-for-a-uiimage
NSInteger pointX = xx;
NSInteger pointY = yy;
CGImageRef cgImage = self.CGImage;
NSUInteger width = self.size.width;
NSUInteger height = self.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
int bytesPerPixel = 4;
int bytesPerRow = bytesPerPixel * 1;
NSUInteger bitsPerComponent = 8;
unsigned char pixelData[4] = { 0, 0, 0, 0 };
CGContextRef context = CGBitmapContextCreate(pixelData, 
                                             1,
                                             1,
                                             bitsPerComponent, 
                                             bytesPerRow, 
                                             colorSpace,
                                             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextSetBlendMode(context, kCGBlendModeCopy);

// Draw the pixel we are interested in onto the bitmap context
CGContextTranslateCTM(context, -pointX, pointY-(CGFloat)height);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
CGContextRelease(context);

CGFloat alpha = (CGFloat)pixelData[3] / 255.0f;
return alpha;
}
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • code added. typed inline so may not be 100% ok... but I also had a good reference :D – Daij-Djan May 23 '13 at 20:26
  • thanks for fast response.. your method looks better already from what I was using. however, the problem is still there :/ I am not getting accurate values of alpha value.. I started thinking that the image itslef may have a problem in its format or something so here is the image maybe you can find somehting [Image](http://s18.postimg.org/48mjhd6u1/roof.png) ... and one more thing by CGImageRef cgImage = self.CGImage; did you mean CGImageRef cgImage = myImage.CGImage; ?? because I am getting an error fro that. thanks again – Alsmayer May 23 '13 at 21:08
  • Well, I am having the same issue I don't get the right alpha value every time. Any idea what could the problem be? – Alsmayer May 24 '13 at 09:10
  • do you use a scaled version of the image? ... maybe via a UIImageView that scales or via a transform or... – Daij-Djan May 24 '13 at 09:28
  • the mode of the image is scale to fill if that is what you mean other than that I don't do any kind of modification or transformatin to the image – Alsmayer May 24 '13 at 15:12
  • Ah so does he get into the first if then -- maybe the scaled to fill image is larger than the original image – Daij-Djan May 24 '13 at 17:18
  • if(alpha<1) yes it does.. but some times it prints that alpha = 0 although it is not transparent – Alsmayer May 24 '13 at 17:55