Rotate Scale Flip – NSImageView

This is a problem which I have fiddled a lot to solve. Sometimes NSImageView objects seems to behave strangely when you rotate them and try to reposition it inside its superview.

The Problem:
I had an NSImageView as subview of another NSView. There where slider controls to scale and rotate the subview as and when required. I also had horizontal and vertical flipping options. So when I played around with it, the subview started acting weirdly.

I had used [subview setFrameCenterRotation:angle]; to rotate the subview.
For flipping NSAffineTransform scaleXBy:yBy: was helpful. Scaling I had set the imageScaling flag,
so that I just have to set the frame and NSImageView takes care of it by itself.

Now why is this acting weirdly? Seems all fine. All proper apis have been used. After some brain wracking and a little help from OpenGL transformation application, I found out the solution.

If you have rotated a image view, before doing any other transformation rotate it back to zero angle and the apply the transforms and then rotate it back to required angle.

Code for flipping images:

- (void)flipImageVertically:(NSImageView*)imageView
{
NSAffineTransform *flipper = [NSAffineTransform transform];
NSSize dimensions = [imageView frame].size;

NSImage *aFrame = [imageView.image copy];

[aFrame lockFocus];
[flipper scaleXBy:1.0 yBy:-1.0];
[flipper set];

[aFrame drawAtPoint:NSMakePoint(0,-dimensions.height)
fromRect:NSMakeRect(0,0, dimensions.width, dimensions.height)
operation:NSCompositeCopy fraction:1.0];

[aFrame unlockFocus];

[imageView setImage:aFrame];

[aFrame release];

}

- (void)flipImageHorizontally:(NSImageView*)imageView
{
NSAffineTransform *flipper = [NSAffineTransform transform];
NSSize dimensions = [imageView frame].size;

NSImage *aFrame = [imageView.image copy];

[aFrame lockFocus];
[flipper scaleXBy:-1.0 yBy:1.0];
[flipper set];

[aFrame drawAtPoint:NSMakePoint(-dimensions.width,0)
fromRect:NSMakeRect(0,0, dimensions.width, dimensions.height)
operation:NSCompositeCopy fraction:1.0];

[aFrame unlockFocus];

[imageView setImage:aFrame];

[aFrame release];
}

Tint an image view

I was developing an editor kind of stuff, where there are multiple images placed in a view and lot of custom drawings happen here and there in the custom views. The user gets to select a view and it has to be highlighted. For this I used to dram a boundary line using NSBezierPath in the view’s drawRect: method. But this used to hide the edges and corners of the view. Users started to complain they cannot place the views pixel perfect. They demanded for tinting the view.

After googling out for some time I found following NSImage extension:

@implementation NSImage (Extends)

- (NSImage *)imageTintedWithColor:(NSColor *)tint
{
if (tint != nil) {
NSSize size = [self size];
NSRect bounds = { NSZeroPoint, size };
NSImage *tintedImage = [[NSImage alloc] initWithSize:size];

[tintedImage lockFocus];

CIFilter *colorGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"];
CIColor *color = [[[CIColor alloc] initWithColor:tint] autorelease];

[colorGenerator setValue:color forKey:@"inputColor"];

CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMonochrome"];
CIImage *baseImage = [CIImage imageWithData:[self TIFFRepresentation]];

[monochromeFilter setValue:baseImage forKey:@"inputImage"];
[monochromeFilter setValue:[CIColor colorWithRed:0.75 green:0.75 blue:0.75] forKey:@"inputColor"];
[monochromeFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"];

CIFilter *compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"];

[compositingFilter setValue:[colorGenerator valueForKey:@"outputImage"] forKey:@"inputImage"];
[compositingFilter setValue:[monochromeFilter valueForKey:@"outputImage"] forKey:@"inputBackgroundImage"];

CIImage *outputImage = [compositingFilter valueForKey:@"outputImage"];

[outputImage drawAtPoint:NSZeroPoint
fromRect:bounds
operation:NSCompositeCopy
fraction:1.0];

[tintedImage unlockFocus];

return [tintedImage autorelease];
}
else {
return [[self copy] autorelease];
}
}

@end

Call this method on the view’s image:
[view.image imageTintedWithColor:[NSColor redColor]];

Hope this is helpful.