NSImage is always being scaled and looks bad - macos

I want to create an NSImage of an NSScrollView object, so I can use the flat graphic for animation purposes.
When I render my scrollview object into a graphic and add it back to my window, it works but looks really bad like it's been scaled to 99% or something. I want the image to not be scaled and 100% pixel accurate. (Note: the image isn't scaled, it's the same size, it just looks like it's been poorly rescaled - the text looks rough and poor compared to the view onscreen in the scrollview)
My code:
(scrollView is my NSScrollView object)
NSData *pdf = [scrollView dataWithPDFInsideRect:[scrollView bounds]];
NSImage *image = [[NSImage alloc] initWithData:pdf];
NSImageView *imageView = [[NSImageView alloc] initWithFrame:[scrollView bounds]];
[imageView setImage: image];
[mainGUIPanel addSubview: imageView];
I've tried a heap of things, messed with pixel sizes, bounds, used IB to create the destination NSView and put the image inside that but just cannot get the image to not look bad. Any ideas?
Edit:
I tried writing the pdf data to a pdf file and viewed it, and it looked ok. So the bitmap image is being captured ok, it's just on the display that it looks like it's being scaled somewhat.
Edit2:
Also tried getting the bitmap like this:
NSBitmapImageRep *bitmap = [scrollView bitmapImageRepForCachingDisplayInRect:[scrollView bounds]];
[scrollView cacheDisplayInRect:[scrollView bounds] toBitmapImageRep:bitmap];
NSImage * image = [[NSImage alloc] initWithSize:[bitmap size]];
[image addRepresentation: bitmap];
Same results - the bitmap looks exactly the same, bad and scaled when displayed.
This leads me to believe that capturing the bitmap data either way works fine, it's creating the view and rendering the image that is doing the scaling. How can I make sure that the view and image are shown at the correct size and scaling?
Edit3:
Ok, I started a new blank project and set this up, and it works perfectly - the new imageview is identical to the grabbed bitmap. So I suspect my issue is stemming from some rendering/compositing issue when drawing the bitmap to the view. Investigating further...
It turns out the issue stems from the scrollView that I am rendering from. This has a transparent background (Draw Background is off in IB) and the text is the scrollView looks good. If I turn "Draw Background ON", with a transparent background color, the text is rendered badly, exactly as it is when I capture the image programatically.
So, in my app, even though Draw Background is off, the scrollView image is captured as though Draw Background is on. So I need to understand why the text is rendered badly when Draw Background is on and set to transparent, and hopefully this will lead me towards a solution.
Also tried creating an NSClipview with background drawing turned off and putting the bitmap view into that, but it sill renders the same. I can't find a way to render the transparent image to the screen without horrible artifacting.

Ok, I've found a solution. Instead of getting a grab of the transparent background scrollview object itself, I'm instead getting a grab of the parent view (essentially the window background), and restricting the bounds to the size of the scrollview object.
This captures both the background, and the contents of the scrollview, and displays correctly without any issues of transparency.

Related

NSImage drawInRect and TemplateImages

In have an NSImage is a template image (that is, [NSImage isTemplate] returns YES).
When I use it inside an NSImageView, it is drawn correctly as a template image.
However, if I draw it manually using drawInRect:fromRect:operation:fraction:, it is drawn as a flat black image.
How can I draw an NSImage manually, and still get the 'template' effect?
The special drawing of template images is part of CoreUI and not public.
You can imitate the effect with Quartz though. Details and code can be found in this answer here on SO:
https://stackoverflow.com/a/7138497/100848

Composite 2 UIImageViews using Core Image, when one ImageView is in a ScrollView

I have a UIScrollView with a UIImageView inside it. The user can pan and zoom the image inside the scrollView.
I also have a UIImageView in the main view (above the scrollView), and the user can move that image around the screen.
I'm using CISourceOverCompositing to combine them both:
-(UIImage *) compositeFinalImage {
CIImage *foregroundImage = [CIImage imageWithCGImage:foregroundImageView.image.CGImage];
CIFilter *composite =[CIFilter filterWithName:#"CISourceOverCompositing"];
[composite setValue: foregroundImage forKey: #"inputImage"];
[composite setValue: [CIImage imageWithCGImage:backgroundImage.CGImage] forKey: #"inputBackgroundImage"];
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *finalImage = [composite valueForKey:#"outputImage"];
return [UIImage imageWithCGImage:[context createCGImage:finalImage fromRect:finalImage.extent] scale:1.0 orientation:userImageOrientation];
}
...but the location and scale of the image has been lost when I make it a CIImage and the foreground image is always at the bottom left.
So I tried to use imageByApplyingTransform to move the position of the foreground image (and eventually I will have to apply scale transform as well), but I get the position from the ImageView, but the coordinates need to be in the native resolution of the background image itself... but the background image is moving around (which I guess means I have to take the ContentOffset into account somehow), and the background image has a certain scale, and the foreground image has a certain scale...
It seems weird that it's needed to re-produce all of the transformation with regards to different scale, rotation and position variables of each image...
This is the basic idea of what I'm trying to do (the left side is in the main view coordinates, while the right side is in native image coordinates).
Any help will be much appreciated!
Thanks!

Change bounds origin + cropping an image

I am a newbie to Cocoa, I have a few doubts regarding NSImage.
Question1:
Changing the bounds origin of an image doesn't seem to have any effect. I expected the image to be drawn from the newly set origin but that doesn't seem to the case. Am I missing something ?
code:
NSImage* carImage = [NSImage imageNamed:#"car"];
[self.imageView setImage:carImage];
//Following line has no effect:
self.imageView.bounds = CGRectMake(self.imageView.bounds.origin.x + 100, self.imageView.bounds.origin.y, self.imageView.bounds.size.width,self.imageView.bounds.size.height);
Note: imageView is an IBOutlet
Question2:
I was trying to crop an image, but it doesn't seem to be cropping the image, I can see the complete image. What is that I am missing ?
code:
NSRect sourceRect = CGRectMake(150, 25, 100, 50);
NSRect destRect = CGRectMake(0, 0, 100, 50);
NSImage* carImage = [NSImage imageNamed:#"car"];
[carImage drawInRect:destRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0];
[self.imageView setImage:carImage];
Thanks
Changing the bounds origin of an image doesn't seem to have any effect. …
//Following line has no effect:
self.imageView.bounds = CGRectMake(self.imageView.bounds.origin.x + 100, self.imageView.bounds.origin.y, self.imageView.bounds.size.width,self.imageView.bounds.size.height);
That's an image view, not an image.
The effect of changing the bounds of a view depends on what the view does to draw. Effectively, this means you shouldn't change the bounds of a view that isn't an instance of a view class you created, since you can't predict exactly how an NSImageView will draw its image (presumably, since it's a control, it involves its cell, but more than that, I wouldn't rely on).
More generally, it's pretty rare to change a view's bounds origin. I don't remember having ever done it, and I can't think of a reason off the top of my head to do it. Changing its bounds size will scale, not crop.
I was trying to crop an image, but it doesn't seem to be cropping the image, I can see the complete image. What is that I am missing ?
[carImage drawInRect:destRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0];
[self.imageView setImage:carImage];
Telling an image to draw does not change anything about the image. It will not “crop the image” such that the image will thereafter be smaller or larger. You are telling it to draw, nothing more.
Consequently, the statement after that sets the image view's image to the whole image, exactly as if you hadn't told the image to draw, because telling it to draw made no difference.
What telling an image to draw does is exactly that: It tells the image to draw. There are only two correct places to do that:
In between lockFocus and unlockFocus messages to a view or image (or after setting the current NSGraphicsContext).
Within a view's drawRect: method.
Anywhere else, you should not tell any Cocoa object to draw.
One correct way to crop an image is to create a new image of the desired/adjusted size, lock focus on it, draw the desired portion of the original image into it, and unlock focus on the new image. You will then have both the original and a cropped version.
Another correct way would be to create your own custom image view that has two properties: One owning an image to draw, and the other holding a rectangle. When told to draw, this custom view would tell the image to draw the given rectangle into the view's bounds. You would then always hold the original image and simply draw only the desired section.

Having trouble making a PNG bitmap from an NSView

Update
Nevermind :-)
Figured it out using bitmapImageRepForCachingDisplayInRect & cacheDisplayInRect:toBitmapImageRep:.
I'll leave the question here for posterity, though.
I'm working on a little application that, among other things, has an NSView subclass that draws a bunch of bezierPaths. I'd like to be able to save the drawn result as either EPS or PNG.
The view is being drawn in an offscreen window (for scaling reasons), and even though it returns the correct EPS data, I can't seem to get any useful bitmap data from it.
EPS is no problem (I simply write the NSData from -dataWithEPSInsideRect: to a file), but I can't seem to get a PNG bitmap.
If I try calling:
[self lockFocus];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:self.bounds];
[self unlockFocus];
return rep;
from a category method I've added to NSView, I get useless white PNG data out of it when I try representationUsingType:NSPNGFileType properties:[NSDictionary dictionary].
Strangely, if I try calling lockFocus/initBitmapWith../unlockFocus from outside the view (or its category methods), I get an exception saying that the view or one of its ancestors is hidden. And well, yes, it's offscreen (the offscreen window's been init'ed with defer:NO, by the way, so it should paint).
So I can either get useless data or I can get an exception. Not awesome.
To add to my confusion: If I make an NSImage containing an EPS representation, and a (useless) white bitmap, where both should be the size of the view's bounds, the bitmap representation is always 20 pixels/units narrower than the bounds. No idea why! The EPS is the correct size.
Since this may all be related to how the offscreen window's created, here's the code for that (from the NSView category)
- (NSWindow*)placeInOffscreenWindow {
NSRect windowBounds = { { -1000.0 , -1000.0 } , self.bounds.size };
NSWindow *hiddenWindow = [[NSWindow alloc] initWithContentRect: windowBounds
styleMask: NSTitledWindowMask | NSClosableWindowMask
backing: NSBackingStoreNonretained
defer: NO];
[[hiddenWindow contentView] addSubview:self];
return hiddenWindow;
}
Any ideas would be appreciated!
Ended up using bitmapImageRepForCachingDisplayInRect: & cacheDisplayInRect:toBitmapImageRep: instead
I suggest you create an NSImage, lock focus on it, tell the view to draw its bounds, and unlock focus, then ask the image to create a CGImage, then pass that to a CGImageDestination to write out the PNG data/file.

NSBezierPath to NSImage in order to avoid CoreAnimation

I have an app that currently has this line:
[myView setWantsLayer:YES];
In order to draw a GUI element via NSBezierPath. This line is required, otherwise when the user types in an adjacent (and overlapping) NSTextField, the contents of myView shudders.
I discovered that calling CoreAnimation loads the OpenGL framework, but does not unload it. See this question.
I think I can get around this by drawing the NSBezierPath to NSImage and then to display the NSImage in lieu of the NSBezierPath, but I haven't found a single source that shows me how to go about this.
Edit:
I should note that I want to save this BEFORE The NSBezierPath is displayed - so solutions that draw an existing view to an NSImage are not useful.
Question:
Can someone point me in the right direction for converting NSBezierPath to an NSImage?
You can draw anything directly into an NSImage, and you can create a blank NSImage. So, create an image whose size is the size of the bounds of the path, translate the path so that it's at the origin of the image, and then lock focus on the image, draw the path, and unlock focus.

Resources