NSView -drawRect and mouse hover performance - macos

I have a custom NSView that draws (with -drawRect) a graph. It also tracks the mouse position (with -mouseMoved and the like) and draws the cursor position/coordinates relative to the axes.
The graph is big and (potentially) slow to draw, and doesn't depend on the mouse position. The mouse-over effect is tiny, and always fast to draw. I don't want to have to redraw everything when the mouse moves a couple pixels, because it feels sluggish.
I'm sure I can make my own private graphics context (doubled in size, if on a 2x display), draw the chart data into that once, and then have -drawRect simply blit that into the view's drawing context. Alternatively, I could split my NSView subclass into two classes, and have one just the chart data, and one just the overlay, and place them exactly on top of each other (though they have to share a bit of data, so this seems awkward).
Is there a built-in method to make this easier, or is there a more idiomatic way of handling this?

Take a look at these NSWindow methods:
Bracketing Drawing Operations
– cacheImageInRect:
– restoreCachedImage
– discardCachedImage
Normally, with a view, you define a drawRect: method that draws contents on-demand. You can, however, do on the spot drawing in a view as result of events for example, by locking focus on the view, performing the drawing, then unlocking focus. The missing link in this on-the-spot drawing, is somehow undoing it without calling your potentially heavy drawRect: via setNeedsDisplay:
This is where the Bracketed drawing operations on NSWindow come into play. Before locking focus on the view and doing some on-the-spot drawing, cache an image of the view in the area you intend to draw. When you next want to update your drawing (while tracking events, perhaps) then restore the cached image, rinse, and repeat.

Related

Direct2D: Check if image is outside visible area before drawing?

Is it a reasonable optimization to omit calls to ID2D1HwndRenderTarget::DrawBitmap() if the image will end up outside the visible area? If I implement the checking logic in the application that will cost some performance, so if the first thing D2D does is doing the same check then I'd rather not do it.
I had a test with my application which renders some UI part using Direct2D (and attaching renderdoc), seems it is a bit random.
I render a mix of Rectangles, Text, Path geometries (beziers) and Rectangle with a bitmap brush (which should be equivalent to your DrawBitmap call).
Then I capture a frame with all those objects visible, and another one panning my UI (using transform) so objects are not visible.
From there could check what is drawn or not:
Text is always culled
Solid color rectangles are not culled
Most of the times path geometries are culled, but sometimes not.
Rectangle with bitmap brush are NEVER culled
So it seems Direct2D is making different decisions depending on the types of elements you plan to draw.
Since rectangles are easily batched and cheap to draw, it seems that they are just drawn regardless.
Bitmap rectangles and text require more work, so it seems they are effectively culled.
Path geometry was looking to depend on how many polygon the geometry is tesselated to (I had a path that was translating to 26 primitives and it was not culled, another one translating to 120 and it got culled).
So you can either trust Direct2D that it will perform that optimization, but I would personally implement a quick rectangle to rectangle check just in case (it's not gonna hurt your performances as its an extremely simple operation).

NSScrollView with massive contentView causing terrible performance on scroll

I'm trying to draw a large waveform/graph that is an NSView placed inside of a NSScrollView. That way, the user can scroll horizontally and parts of it at a time.
The waveform view can be very large (technically, infinitely wide).
If the entire waveform is drawn, the scrolling performance is unusable. I'm unsure why NSScrollView is attempting to redraw the entire view rather than just the visible rect - but I suspect I will need to implement this logic myself.
What is the most efficient way to implement this to have a good scrolling experience?
You could tile the contents of your scroll view into several small NSView instances, each one representing a small part of your graph and placed right beside one another.
However, I am not sure to what extent the views that are clipped off-screen still consume resources and affect performance.
A better apporach would be similar to the above, but relying on the built-in functionality of a Collection View. Its machinery definitely takes care of displaying only the cells that should be visible (not clipped by the scroll view).

Custom shaped NSButton

I am trying to create eight custom buttons (NSButton) in Xcode 4.6.3. Those are the segments of a circle. I used a standard rectangular button for each of them, adding a custom image for each segment. However, when I put the pieces together in one circle, there is no way to click some of these buttons, as the rectangular areas around each of them overlap, and prevent from reaching the other half of the buttons.
I was wondering if there is any way to make the button shape at least triangular, such that I can click on all of these buttons?
From the documentation "View Programming Guide":
Note: For performance reasons, Cocoa does not enforce clipping among sibling views or guarantee correct invalidation and drawing behavior when sibling views overlap. If you want a view to be drawn in front of another view, you should make the front view a subview (or descendant) of the rear view.
In other words, you can't expect overlapping views to process mouse events properly. There's no way of getting around the fact that views occupy rectangular frames. You have to make a single view which performs the work of all of your circle segments (including drawing and event handling, and optionally mouse moved events). YOu will have to use trigonometry to calculate which segment a mouse click occurs in, and respond appropriately as though a button were pressed, by re-drawing the segment and invoking the desired action.

Best way to obtain a scrollable OpenGL view with Cocoa

Say I'm writing a 2D cad program of some sort, and I want to be able to zoom in and scroll around my document. However, I also want full control over how my document is drawn and I want an OpenGL context for which to do the drawing. How do I do this? Should I subclass NSScrollView and do something I can't quite figure out there? Should I subclass NSOpenGLView and add a pair of NSScrollers and figure out how to draw them properly? Making NSScrollers and drawing them in a way that looks good natively looks nontrivial, but NSScrollViews seem to want to own all the content you might be scrolling, rather than letting me control the size of the knob of the scroll bar and other such things. I'd be completely content with giving a document size in pixels or some such, just the most important thing to me is that when I draw to (0,0) in my OpenGL context, I draw to the corner of the window, and not into some buffer that NSScrollView owns.
Should I subclass NSOpenGLView and add a pair of NSScrollers
Yes, since scrolling a OpenGL view doesn't make sense. You want to adjust the viewing volume (i.e. the parameters defining the projection matrix), rather than moving your viewport around. And that only works if you have manual control over the scroll bars.

WP7 XNA: Does Draw method

sorry for a dumb question. We put every drawing objects in Draw method, does all objects get drawn each time even some of them not changed? Does it need some kind of back buffer and draw at once to reduce flickering? or at least add if-else reduce drawing unchanged objects?
Draw is called every frame and yes it will re-draw the entire screen even if an object has not been moved or similar.

Resources