Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>To expand on quixoto's answer and because I have relevant source code from a project of my own, if you were to need to drop to on-CPU pixel manipulation then the following, which I've added exposition to, should do the trick:</p> <pre><code>@implementation UIImage (NegativeImage) - (UIImage *)negativeImage { // get width and height as integers, since we'll be using them as // array subscripts, etc, and this'll save a whole lot of casting CGSize size = self.size; int width = size.width; int height = size.height; // Create a suitable RGB+alpha bitmap context in BGRA colour space CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); unsigned char *memoryPool = (unsigned char *)calloc(width*height*4, 1); CGContextRef context = CGBitmapContextCreate(memoryPool, width, height, 8, width * 4, colourSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colourSpace); // draw the current image to the newly created context CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]); // run through every pixel, a scan line at a time... for(int y = 0; y &lt; height; y++) { // get a pointer to the start of this scan line unsigned char *linePointer = &amp;memoryPool[y * width * 4]; // step through the pixels one by one... for(int x = 0; x &lt; width; x++) { // get RGB values. We're dealing with premultiplied alpha // here, so we need to divide by the alpha channel (if it // isn't zero, of course) to get uninflected RGB. We // multiply by 255 to keep precision while still using // integers int r, g, b; if(linePointer[3]) { r = linePointer[0] * 255 / linePointer[3]; g = linePointer[1] * 255 / linePointer[3]; b = linePointer[2] * 255 / linePointer[3]; } else r = g = b = 0; // perform the colour inversion r = 255 - r; g = 255 - g; b = 255 - b; // multiply by alpha again, divide by 255 to undo the // scaling before, store the new values and advance // the pointer we're reading pixel data from linePointer[0] = r * linePointer[3] / 255; linePointer[1] = g * linePointer[3] / 255; linePointer[2] = b * linePointer[3] / 255; linePointer += 4; } } // get a CG image from the context, wrap that into a // UIImage CGImageRef cgImage = CGBitmapContextCreateImage(context); UIImage *returnImage = [UIImage imageWithCGImage:cgImage]; // clean up CGImageRelease(cgImage); CGContextRelease(context); free(memoryPool); // and return return returnImage; } @end </code></pre> <p>So that adds a category method to UIImage that:</p> <ol> <li>creates a clear CoreGraphics bitmap context that it can access the memory of</li> <li>draws the UIImage to it</li> <li>runs through every pixel, converting from premultiplied alpha to uninflected RGB, inverting each channel separately, multiplying by alpha again and storing back</li> <li>gets an image from the context and wraps it into a UIImage</li> <li>cleans up after itself, and returns the UIImage</li> </ol>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload