views:

2556

answers:

4

UIImage has a read-only property CGImage. I have to read its pixels to a memory block and edit them and then make a new UIImage to replace the old one. I want to know if there is a way bypass the read-only property and edit those pixels directly.

Thanks.


Thanks all. I have found a way to do it. Write a class with those method:

-(void)preProcess:(UIImage*)srcImage {
    m_Context = ...// Created by calling CGBitmapContextCreate(...)
    ...
    CGContextDrawImage(m_Context, rect, srcImage.CGImage);
    m_Bits = (unsigned char*)CGBitmapContextGetData (mContext);
}

-(void)postProcess {
    CGContextRelease(m_Context);
    free(m_Bits);
}

-(UIImage*)doProcess:(CGPoint)pt {// just a example 
    unsigned char* ppxl = m_Bits + ...
    // do something...
    CGImageRef imRef = CGBitmapContextCreateImage(mContext);
    return [UIImage imageWithCGImage:imRef];
}

And preProcess and postProcess are called just once.

+1  A: 

The short answer is no. However, you say you have to make a copy anyhow, so why not just get an NSData object and manipulate its bytes.

From the Apple docs on UIImage:

Because image objects are immutable, they also do not provide direct access to their underlying image data. However, you can get an NSData object containing either a PNG or JPEG representation of the image data using the UIImagePNGRepresentation and UIImageJPEGRepresentation functions.

To get the data as a PNG, use:

NSData * UIImagePNGRepresentation (
   UIImage *image
);

for JPEG, use:

NSData * UIImageJPEGRepresentation (
   UIImage *image,
   CGFloat compressionQuality
);
Matt Long
You really don't have to sign your posts. We know who you are, your name and picture are less than 300px to the right of your extra sig. You also totally failed to explain why he can't directly edit the pixels or why it's read only in the first place.
Sneakyness
I would say you've just been awarded the "Captain Kranky Pants" badge. Re-read the question. I assure you. He did not ask 'why'. -Matt (<-- oops. that darned extra sig again.)
Matt Long
Matt certainly did answer why it's read-only - he quoted from the Apple docs, specifically saying images are considered immutable. Given that as a constraint, any modification you wanted to make would have to be on a copy.However, the answer is not correct. You would not be getting the raw pixels in this case, but a compressed version of the image as a PNG/JPG. Unless you implement a JPG/PNG decompression algorithm, you will not be able to modify the pixel data.
Itay
Thanks for the clarification Itay. That's quite helpful. I was forgetting that part. You're absolutely right. You would have the compressed data at that point and not the image data. Very nice solution. I voted you up! ;-)
Matt Long
Matt's excerpt is helpful for me.
Cook Schelling
+8  A: 

You cannot get at the original pixels. However, you can get a copy. One option is to do what Matt suggested, and convert it into a PNG/JPG - though remember, the image is now compressed, and you will be manipulating the compressed file and not the pixels directly.

If you want to get at a copy of the raw pixels, you can do something like:

UIImage* image = ...; // An image
NSData* pixelData = (NSData*) CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
void* pixelBytes = [pixelData bytes];

// Take away the red pixel, assuming 32-bit RGBA
for(int i = 0; i < [pixelData length]; i += 4) {
 bytes[i] = 0; // red
 bytes[i+1] = bytes[i+1]; // green
 bytes[i+2] = bytes[i+2]; // blue
 bytes[i+3] = bytes[i+3]; // alpha
}

Now, if you wanted to make this into a new UIImage, you can do something like:

NSData* newPixelData = [NSData dataWithBytes:pixelBytes length:[pixelData length]];
UIImage* newImage = [UIImage imageWithData:newPixelData]; // Huzzah
Itay
I tried but failed. CGDataProviderCopyData return a CFDataRef which is a reference to an immutable CFData object according to the iPhone doc.
Cook Schelling
+3  A: 

This may help you out.

When you're done, you can use CGImageCreate to create a CGImageRef, then use +[UIImage imageWithCGImage:] to create a new UIImage.

Dave R
+3  A: 

For the more obtuse among us (read: future me) here's some working code based on Itay and Dave R's answers. It starts with a UIImage and ends with a modified UIImage:

    // load image
    UIImage *image      = [UIImage imageNamed:@"test.png"];
    CGImageRef imageRef = image.CGImage;
    NSData *data        = (NSData *) CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
    char *pixels        = (char *)[data bytes];

    // manipulate the individual pixels
    for(int i = 0; i < [data length]; i += 4)
    {
        int r = i;
        int g = i+1;
        int b = i+2;
        int a = i+3;

        pixels[r]   = 0;
        pixels[g]   = pixels[g];
        pixels[b]   = pixels[b];
        pixels[a]   = pixels[a];
    }

    // create a new image from the modified pixel data
    size_t width                    = CGImageGetWidth(imageRef);
    size_t height                   = CGImageGetHeight(imageRef);
    size_t bitsPerComponent         = CGImageGetBitsPerComponent(imageRef);
    size_t bitsPerPixel             = CGImageGetBitsPerPixel(imageRef);
    size_t bytesPerRow              = CGImageGetBytesPerRow(imageRef);

    CGColorSpaceRef colorspace      = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo         = CGImageGetBitmapInfo(imageRef);
    CGDataProviderRef provider      = CGDataProviderCreateWithData(NULL, pixels, [data length], NULL);

    CGImageRef newImageRef = CGImageCreate (
                              width,
                              height,
                              bitsPerComponent,
                              bitsPerPixel,
                              bytesPerRow,
                              colorspace,
                              bitmapInfo,
                              provider,
                              NULL,
                              false,
                              kCGRenderingIntentDefault
                              );
    // the modified image
    UIImage *newImage   = [UIImage imageWithCGImage:newImageRef];

    // cleanup
    free(pixels);
    CGImageRelease(imageRef);
    CGColorSpaceRelease(colorspace);
    CGDataProviderRelease(provider);
    CGImageRelease(newImageRef);
Shaun Inman