Images in NSImageViews randomly turned upside-down
-
15-10-2019 - |
Question
So, I have this odd issue in my Cocoa app. A toolbar item, and some NSImageViews just render their image upside-down for no apparent reason. The toolbar item actually comes that way when I launch the App for some reason (the icon is assigned in code), and then, when I open my HUD with a finder zoom effect, the imageViews contained in it are upside down.
That image above is a screensot of this odd phenomenon. AS you can see, the "Show Trash" item is rotated oddly, every NSImageView in the HUD, but the NSImageView in the NSBrowser is fine.
How would I go about tracking down what is causing this, and then fixing it?
Solution
See Mac OS X Developer Release Note - AppKit Release Notes (Snow Leopard):
Flipped images in toolbars (New since January 2009 seed)
A common but incorrect practice is to call
setFlipped:YES
on an image that you plan to draw into a flipped graphics context. This is incorrect because if the image is later drawn into a normal unflipped context, the image will appear upside down. However, Leopard and earlier contained a bug in which images set onNSToolbarItem
(viasetImage:
) would ignore theirisFlipped
property. SnowLeopard fixes this bug for apps compiled on SnowLeopard with the 10.6 SDK; as a result, some images which should have drawn upside down in Leopard, will begin doing so when your app is recompiled.If you recompile your app on SnowLeopard and discover that your toolbar item images are drawing upside down, then it indicates that you are calling
setFlipped:YES
somewhere within your code. You should remove those calls and replace the image drawing with methods that correctly handle flipped contexts. See the discussion ofsetFlipped:
in these Release Notes for more discussion.
Re: setFlipped:
NSImage: deprecating -[NSImage setFlipped:], adding a drawing method that respects context flippedness (New since WWDC 2008)
The flipped attribute of
NSImage
is widely misunderstood. We are deprecating it for SnowLeopard, and replacing its typical uses with less error-prone API.The property describes the orientation of the internal coordinate system of the
NSImage
. Just as a superview never cares about the flippedness of its subviews, a user of anNSImage
should not care about its flippedness.The typical (flawed) use case is to try to call
[image setFlipped:[[NSGraphicsContext currentContext] isFlipped]]
just prior to drawing, but this does not accomplish the intended goal. If called before caching, then representations end up caching upside down, and the flip is absorbed into the cache. If called after caching, it has no effect-the cached representation is already supposed to incorporate any necessary flipping. In the former case, if theNSImage
is drawn anywhere else later, it ends up upside down in that place, which is also confusing because the bug and the expression of the bug are far apart. Lack of understanding regarding flippedness is also frequently the source of poorly performing code, in which people make unnecessary intermediate buffers to work around perceived framework bugs. The framework behaves according to design, but contrary to expectation, and the semantics are not all that useful. It's also difficult to change the semantics of-[NSImage isFlipped]
, because a lot of code is very closely dependent on the current behavior. Rather than attempt this, we have deprecated the property.We are providing a simple and correct way to draw images in a flipped or unflipped context, which is a draw method that can account for context flippedness. We are also adding a hints parameter matching the hints in
-bestRepresentationForRect:context:hints:
.
- (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha respectFlipped:(BOOL)respectContextIsFlipped hints:(NSDictionary *)hints;
Pass
YES
forrespectFlipped
to get the fancy new behavior. One note for those that understand the CTM and worry that this method has an odd interaction, where modifying the CTM could fail to have any effect on image drawing: This is not the case. This method branches behavior based on[[NSGraphicsContext currentContext] isFlipped]
. Modifying the CTM might turn your axes upside down, but it will not alter the result of-[NSGraphicsContext isFlipped]
. They're completely orthogonal.A second valid use of
-[NSImage setFlipped:]
was to specify the flippedness of the context obtained via-[NSImage lockFocus]
. There are cases, for example drawing directly viaNSLayoutManager
, that require a flipped context. To cover this case, we add
- (void)lockFocusFlipped:(BOOL)flipped;
This doesn't alter the state of the image itself, only the context on which focus is locked. It means that (0,0) is at the top left and positive along the Y-axis is down in the locked context.
OTHER TIPS
My off-the-top-of-my-head guess is that the image has mistakenly had -setFlipped:YES
called on it.