1
votes

I have an unmasked CGImage, using four bytes RGBA per pixel, whose first few pixels are:

C5 D2 D4 FF
C7 D4 D6 FF
C8 D5 D6 FF
C6 D4 D3 FF
C7 D5 D4 FF

I then make a masking image in greyscale (i.e. one byte per pixel)

00
00
00
00
00

Finally I call CGImageCreateWithMask to apply the mask. The first few pixels of the resulting masked image are:

C5 D2 D4 FF
C7 D4 D6 FF
C8 D5 D6 FF
C6 D4 D3 FF
C7 D5 D4 FF

If I convert this CGImage to a UIImage and add it to an image control these first few pixels are correctly 100% transparent, and yet their underlying pixels are incorrectly 100% opaque.

This is the pattern I am using to look at the bytes of any CGImage.

let imageCGDataProvider:CGDataProvider? = CGImageGetDataProvider(imageCG)
let imageCGPixelData:CFData? = CGDataProviderCopyData(imageCGDataProvider!)
let imageCGData:UnsafePointer<UInt8> = CFDataGetBytePtr(imageCGPixelData!)

How do I get the byte values of the pixels in the masked CGImage, i.e. the byte values with the mask applied?

1

1 Answers

1
votes

This is pretty interesting. It appears that the masking is only applied to the image when it is drawn.

If you do a pointer comparison on the original image's data provider, and the masked image's data provider, you'll see that they both have the same data provider. I can only assume the mask info is stored internally, and is only accessed when it comes to draw the actual image.

This doesn't make too much sense to me, as a design decision. If you have to draw the same masked image multiple times, then you'll have to re-mask the same data multiple times (although CGImage may internally cache the result). This would make great sense if you were able to un-mask or re-mask an already masked CGImage – but AFAIK you can't.

I initially hoped that CGImageCreateWithImageInRect() or CGImageCreateCopy() would force the CGImage to apply the mask to the underlying data – but it just seems to pass the same data provider, along with the assumed internal mask info.

The only way I could find to get out the masked data, was to actually draw the image in a bitmap context. For example:

let ctx = CGBitmapContextCreate(nil, CGImageGetWidth(img), CGImageGetHeight(img), 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo.rawValue)
CGContextDrawImage(ctx, CGRect(origin: CGPointZero, size: CGSize(width: CGImageGetWidth(img), height: CGImageGetHeight(img))), img)
let data = UnsafeMutablePointer<UInt8>(CGBitmapContextGetData(ctx))

If anyone else knows more about this – I'd definitely be interested to know!