A little more detail…
I posted earlier this evening with a consolidation and small addition to what had been said on this page – that can be found at the bottom of this post. I am editing the post at this point, however, to post what I propose is (at least for my requirements, which include modifying pixel data) a better method, as it provides writable data (whereas, as I understand it, the method provided by previous posts and at the bottom of this post provides a read-only reference to data).
Method 1: Writable Pixel Information
-
I defined constants
#define RGBA 4 #define RGBA_8_BIT 8
-
In my UIImage subclass I declared instance variables:
size_t bytesPerRow; size_t byteCount; size_t pixelCount; CGContextRef context; CGColorSpaceRef colorSpace; UInt8 *pixelByteData; // A pointer to an array of RGBA bytes in memory RPVW_RGBAPixel *pixelData;
-
The pixel struct (with alpha in this version)
typedef struct RGBAPixel { byte red; byte green; byte blue; byte alpha; } RGBAPixel;
-
Bitmap function (returns pre-calculated RGBA; divide RGB by A to get unmodified RGB):
-(RGBAPixel*) bitmap { NSLog( @"Returning bitmap representation of UIImage." ); // 8 bits each of red, green, blue, and alpha. [self setBytesPerRow:self.size.width * RGBA]; [self setByteCount:bytesPerRow * self.size.height]; [self setPixelCount:self.size.width * self.size.height]; // Create RGB color space [self setColorSpace:CGColorSpaceCreateDeviceRGB()]; if (!colorSpace) { NSLog(@"Error allocating color space."); return nil; } [self setPixelData:malloc(byteCount)]; if (!pixelData) { NSLog(@"Error allocating bitmap memory. Releasing color space."); CGColorSpaceRelease(colorSpace); return nil; } // Create the bitmap context. // Pre-multiplied RGBA, 8-bits per component. // The source image format will be converted to the format specified here by CGBitmapContextCreate. [self setContext:CGBitmapContextCreate( (void*)pixelData, self.size.width, self.size.height, RGBA_8_BIT, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast )]; // Make sure we have our context if (!context) { free(pixelData); NSLog(@"Context not created!"); } // Draw the image to the bitmap context. // The memory allocated for the context for rendering will then contain the raw image pixelData in the specified color space. CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height } }; CGContextDrawImage( context, rect, self.CGImage ); // Now we can get a pointer to the image pixelData associated with the bitmap context. pixelData = (RGBAPixel*) CGBitmapContextGetData(context); return pixelData; }
Read-Only Data (Previous information) – method 2:
Step 1. I declared a type for byte:
typedef unsigned char byte;
Step 2. I declared a struct to correspond to a pixel:
typedef struct RGBPixel{
byte red;
byte green;
byte blue;
}
RGBPixel;
Step 3. I subclassed UIImageView and declared (with corresponding synthesized properties):
// Reference to Quartz CGImage for receiver (self)
CFDataRef bitmapData;
// Buffer holding raw pixel data copied from Quartz CGImage held in receiver (self)
UInt8* pixelByteData;
// A pointer to the first pixel element in an array
RGBPixel* pixelData;
Step 4. Subclass code I put in a method named bitmap (to return the bitmap pixel data):
//Get the bitmap data from the receiver's CGImage (see UIImage docs)
[self setBitmapData: CGDataProviderCopyData(CGImageGetDataProvider([self CGImage]))];
//Create a buffer to store bitmap data (unitialized memory as long as the data)
[self setPixelBitData:malloc(CFDataGetLength(bitmapData))];
//Copy image data into allocated buffer
CFDataGetBytes(bitmapData,CFRangeMake(0,CFDataGetLength(bitmapData)),pixelByteData);
//Cast a pointer to the first element of pixelByteData
//Essentially what we're doing is making a second pointer that divides the byteData's units differently - instead of dividing each unit as 1 byte we will divide each unit as 3 bytes (1 pixel).
pixelData = (RGBPixel*) pixelByteData;
//Now you can access pixels by index: pixelData[ index ]
NSLog(@"Pixel data one red (%i), green (%i), blue (%i).", pixelData[0].red, pixelData[0].green, pixelData[0].blue);
//You can determine the desired index by multiplying row * column.
return pixelData;
Step 5. I made an accessor method:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}