views:

335

answers:

1

I'm trying to draw a standard NSImage in white instead of black. The following works fine for drawing the image in black in the current NSGraphicsContext:

NSImage* image = [NSImage imageNamed:NSImageNameEnterFullScreenTemplate];
[image drawInRect:r fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];

I expected NSCompositeXOR to do the trick, but no. Do I need to go down the complicated [CIFilter filterWithName:@"CIColorInvert"] path? I feel like I must be missing something simple.

Anders

+2  A: 

The Core Image route would be the most reliable. It's actually not very complicated, I've posted a sample below. If you know none of your images will be flipped then you can remove the transform code. The main thing to be careful of is that the conversion from NSImage to CIImage can be expensive performance-wise, so you should ensure you cache the CIImage if possible and don't re-create it during each drawing operation.

CIImage* ciImage = [[CIImage alloc] initWithData:[yourImage TIFFRepresentation]];
if ([yourImage isFlipped])
{
    CGRect cgRect    = [ciImage extent];
    CGAffineTransform transform;
    transform = CGAffineTransformMakeTranslation(0.0,cgRect.size.height);
    transform = CGAffineTransformScale(transform, 1.0, -1.0);
    ciImage   = [ciImage imageByApplyingTransform:transform];
}
CIFilter* filter = [CIFilter filterWithName:@"CIColorInvert"];
[filter setDefaults];
[filter setValue:ciImage forKey:@"inputImage"];
CIImage* output = [filter valueForKey:@"outputImage"];
[output drawAtPoint:NSZeroPoint fromRect:NSRectFromCGRect([output extent]) operation:NSCompositeSourceOver fraction:1.0];

Note: release/retain memory management is left as an exercise, the code above assumes garbage collection.

If you want to render the image at an arbitrary size, you could do the following:

NSSize imageSize = NSMakeSize(1024,768); //or whatever size you want
[yourImage setSize:imageSize];
[yourImage lockFocus];
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, imageSize.width, imageSize.height)];
[yourImage unlockFocus];
CIImage* image = [CIImage imageWithData:[bitmap TIFFRepresentation]];
Rob Keniger
Thanks, that does solve the color inversion issue. Unfortunately, it doesn't give me the full resolution: it appears that the initial TIFFRepresentation is much smaller than the size I want to draw it in. How can I make sure I get the data at a high resolution?
smartgo
You could create an `NSBitmapImageRep` from the `NSImage` using the size you want and then create the `CIImage` from the bitmap, see here: http://pastie.org/798088
Rob Keniger
Perfect, thanks so much!
smartgo
No worries. I added the code from pastie.org to my answer so that people don't have to go hunting.
Rob Keniger
-[NSImage lockFocus] modifies the original image. It's like drawing the image in a context set up like a window the size of the image, leaving focus locked there for more drawing, then snapshotting the result back into the image in unlockFocus. You'll lose data here if the original image is anything other than a bitmap with one slice. It's also quite inefficient - lockFocus creates a new buffer, as described. Then TIFFRepresentation creates an uncompressed copy of your image - basically another buffer. Then you initialize CIImage with that, another copy to decode the encoded TIFF data.
Ken
Use -CGImageForProposedRect:context:hints:. No copies.
Ken
Thanks for the info. Unfortunately, that method is only available in 10.6. Is there a better way in Leopard?
Rob Keniger