I used the owner-drawn strategy on CMyListBox class which derives from CListBox. I only want the DrawItem() method to perform when I insert an item in the listbox. But the method is invoked many times. How can I change to invoke it whenever I need.
You could always cache the initial drawing by outputting the content to an in-memory bitmap and then drawing that, it does mean you need to track when something has changed so you can run the actual rending code agaain. It does save running through your render code everytime if there's a lot of it.
I've done exactly what Kieron suggests by caching bitmaps, but only in very expensive rendering code. I actually have to keep multiple cached "states" depending on if an item is highlighted, disabled, normal, etc (this is for toolbar buttons, not listitem - but I think it applies). I only cache the pre-rendered image when I first need it - that way I only cache "states" that I actually need.
My drawing was pure GDI calls. Mostly bitmap manipulations and other drawing that just takes time, plus I was being redrawn much too often (for no good reason - long story).
Changing the fundamentals in the framework I was using (MFC and Stingray) was just not an option. The caching was a last resort after all other optimizations weren't good enough (damn slow virtual machines!!).
Normally drawing is fast enough to do when you're invalidated (DrawItem in this case). I would take a look at what exactly you're doing in DrawItem. I would look into caching data and calculations that are needed by rendering and not the rendering itself (eg the final bitmaps) unless there are no other options.
Also, I read the Vista rending is more optimized, they cache what you've drawn on your window to reduce the contain invalidate/redraw cycle when, for example, a window is moved from behind another.
The DrawItem() method is called whenever there is a requirement to draw any given item in the listbox. If you do not respond to it you are likely to get a blank area in your list box, where the drawn data has been erased and you have not refreshed it. If you really do not think the drawing is necessary, you could do something like
void CMyListBox::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
if (!m_DrawingEnabled)
return;
}
Where m_DrawingEnabled is a member you maintain to stop unnecessary draws,
Related
As an example, imagine a complex snap operation, consisting of two active snappings in a context. Here two indicators on the second image show, that we are snapping perpendicularly and that we are snapping to any point of the line. When we drag out from the snapping intersection, we are not snapping to a point anymore, but we are still snapping perpendicularly to the reference line. For such and similar situations I would like to extend the cursor with different indicators, based on the context, like on these images.
Is it possible in MFC? Or otherwise in a Windows application?
Extending the cursor is not supported, you can only load one. So the best approach is to create all the cursors as .CUR files and then load them as needed.
Cursors can be created programmatically in Win32. The CreateCursor() function creates a cursor taking its dimensions, its hot spot and its AND (black) and XOR (invert) masks as parameters. Therefore you can create or load the basic pointer cursor masks and then add the indicators (either draw them using GDI, if they are simple, or load them from resources), creating the additional cursors you may need. I think it's a quite heavy job to do all these on the fly, so it would best to create all possible indicators during initialization.
The CreateCursor() function seems to create only monochrome cursors, maybe the CreateIconIndirect() function can create color cursors. Also take a look into this Win32 documentation topic: Using Cursors.
Of course this is quite an amount of work. You decide if it's worth or not...
I have a custom Windows control subclassed from CButton (no idea why that was selected--this is 17-year-old code; no semblance of button functionality is present).
Its DrawItem( LPDRAWITEMSTRUCT pdis ) method is called by CButton::OnChildNotify in response to WM_DRAWITEM. It renders its scene with the DC CDC::FromHandle( pdis->hDC ).
The mouse event method OnMouseMove() calculates the new cursor position and calls RedrawWindow( NULL, NULL, RDW_INVALIDATE ). A cursor that follows the mouse duly appears at the new mouse position. It works fine, but it's slow. In fact, only the previous and new cursor cells need be redrawn (if that) yet the graphic updates start to lag as the entire scene is rendered many times.
I thought in my OnMouseMove() method, instead of repainting the entire scene, could just paint the cells in question. It already has the exact X and Y coordinates of the cells and pointers to their data. I thought CPaintDC(this) would provide a DC that allowed this, but it doesn't paint. (Doesn't crash either, which is a rare joy.)
My hazy recollection is that the "optimal" way to do this would be to invalidate just the areas of the two cells, and the DrawItem() method would eventually be told these areas were invalidated, and rather than totally repainting it could just work out from the coordinates which cells they were (not an easy operation btw) and repaint them, and that would streamline not only this cursor problem but also ensure only a few cells be painted were the partially-obscured control partially revealed. But time pressure doesn't allow and the use cases don't seem to call for this to be optimized.
So the question is: is there some nice way for OnMouseMove() to re-render a single control immediately, and if so with what DC? (For instance can I cache the DC that I've received in DrawItem() via FromHandle()?
Right now the only idea I have is to have an object member pointing to a single cell to be redrawn, to call RedrawWindow() with this RDW_UPDATENOW flag, and have DrawItem(), if that flag be set, do just that one item. That would result in DrawItem() getting a DC that presumably would work in the way it always has. Seems like a real hack though, is there a better way?
In a Windows application, it is customary to perform all rendering in response to a WM_PAINT (or WM_NCPAINT) message. Code that needs to trigger a repaint marks part or all of window's client area as dirty, by calling InvalidateRect (and friends). The system is optimized for this approach, coalescing multiple requests into a single update region, and subsequently issuing a WM_PAINT message, when there is no more important work to do (like handling input).
This works reliably, and is usually easier to implement than spreading the rendering across several places. It is, however, perfectly legal to deviate from this, and perform rendering anywhere in your code. While WM_PAINT messages can still arrive at any time, it is desirable to have the out-of-band rendering produce identical visual results as the WM_PAINT handler would, to prevent visual artifacts.
All rendering goes through an abstraction called a device context (DC). When handling a WM_PAINT message in an MFC application, a suitable DC can be obtained by constructing a CPaintDC instance. When rendering anywhere else you cannot use a CPaintDC, but need to use a CClientDC instead (or a CWindowDC, to render the non-client area as well). In general, rendering code need not know, which type of DC it is rendering to, and can usually be reused without change.
I read the following in the Qt Documentation.
Qt documentation on QPainter
The original question on SO, that I looked into.
So, I had two classes, with their own paint() functions. The paint functions would be called upon receiving their respective paint events, that were triggered on different and independent actions by the user. This worked fine.
Now for some reason, I need to show and update both the objects at the same time.
So simply, adding both of the items to the scene does not work. Only one of them is shown and updated. Refactoring the code is not an issue for me. I can re-arrange the two classes so that they are both drawn from one paint().
But this really makes me wonder then, and this is my question (for which I've googled a bit too), how are scenes with many dozens of objects then painted simultaneously (at least they give an illusion of concurrency)? Using threads somehow or through some time-based interleaving?
Maybe it's a silly question. I dunno.
It is indeed a silly question about an imaginary problem that doesn't not exist in reality. The graphics view will schedule consecutive draws for the items in the order needed to produce the desired result. Now if your code doesn't implement the desired result, that's a whole different subject. There is no concurrency, those are consecutive operations that only take place in the main thread.
If your drawing is very complex, draw using a secondary thread on a QImage and use the QImage as cache to draw your items in their respective paint functions.
Now for some reason, I need to show and update both the objects at the
same time.
What might that reason be? What does "at the same time" mean? In a single frame? Is a millisecond apart too much to qualify for "at the same time"?
Re QWidget painting: The paint events are delivered to individual widgets by the widget compositor. The way it works with the default raster back end is as follows: The topmost widget in the hierarchy is backed by a QImage. When any of the sub-widgets are to be repainted, the compositor delivers composite paint events to the widgets that overlay the area to be repainted. This is done sequentially as the compositor traverses the widget graph.
Re QGraphicsItem painting: The paint "events" are delivered to individual items by the scene. The items to be painted are selected basing on what area needs updating, what items were explicitly marked for update, etc. The painter is set up to correctly composite the item with the rest of the scene. The calls to paint are done sequentially as the scene traverses the item graph.
It would be, in general, impossible to do these in parallel due to data dependencies, and the fact that there are no requirements for the paintEvent or paint to be thread-safe.
Your problem is not directly related to this at all, you need to show a complete code example that reproduces your issue. Most likely your implementation of the item ignores some of the requirements for the item's behavior.
I am using QGraphicsScene to draw millions of polygons. I plan to use Qt+OpenGL later, but for now I am not drawing more than 1 million polygons and Qt handles it just fine. The problem arises when I try to display text inside the polygons.
Each polygon (a custom class inheriting QGraphicsPolygonItem) is a visual representation of an object, and has pointer to that object associated with it. Each object has a std::string identifier. If I could simply display that string inside the polygons, it should be fine memory-wise. However, Qt seems to need a QString instead, and it takes a lot of time and space to convert every string. I am creating a QGraphicsTextObject for each polygon and each of those needs a QString copy of the std::string. Is there any way to bypass this copy using just Qt?
Cropping the scene is not desirable. There are some polygons that are too small for any text to fit inside them, and one can only see them by zooming in the scene. These polygons (and their text) need not be displayed (unless the user zooms in), but I don't think that would help without creating various other issues first.
P.S.: Displaying the text on-demand (as the user hovers the mouse over each polygon, for instance) is possible, however it'd be ideal if the text was readily displayed.
Have you profiled it to see if the conversion is actually your bottleneck? Font rendering isn't exactly fast, and if actually tries to render millions of texts, that will be slow. You won't get around the string conversions, the only thing I could think of is to optimize when to do them, and how often.
First, I'd consider to use a custom item either painting the text manually by reimplementing QGraphicsItem::paint, or deriving from e.g.
QGraphicsSimpleTextItem, which allows more tweaking than using QGraphicsSimpleTextItem or even QGraphicsTextItem directly, both which require you to call setText() and thus convert the string upfront.
One thing to be conscious about is when the conversion would be done.
With a custom item, you wouldn't need to do the std::string to QString conversion upfront (when calling setText()), but you could store the std::string and do it on demand only, in your paint() implementation, i.e. convert on first paint() call and then cache.
Another potentially expensive calculation is boundingRect(). This could be tweaked by returning a less precise approximation of the actual text shape. It doesn't hurt if the returned rectangle is somewhat bigger than the actually painted rectangle, it just shouldn't be smaller. So one could use a hardcoded height * approx. letter width + some padding.
Then, text not drawn at all is even cheaper. If the view is so zoomed out, that 10^6 items are drawn (and only then paint() is called), one will have a hard time to read any text. I'd reimplement paint() and make use of QGraphicsView's level-of-detail mechanism (see here) and just not convert the string nor paint anything below a certain level of detail/zoom level.
If you can use Qt 5.4, QGraphicsScene::minimumRenderSize might also come handy. But that alone won't avoid the string conversions nor the boundingRect() when not combined with the other suggestions above.
I can turn the .visible value for CCNodes, but I wonder, can an insivisible node consume less memory/processing than a visible one? Can I set the .visible property to NO when my objects are outside the screen to optimize? Or does cocos2d already do that stuff for me?
Invisible nodes are typically skipped when it comes to being rendered. On the other hand, nodes with visible set to YES will invoke OpenGL draw calls, regardless of whether they are on or off the screen (See Riq's comment here). ie. cocos2d does not seem to perform any kind of culling for offscreen elements.
If this is indeed the case, I would simply just set visible = NO (no harm and definitely not hard!) if they are completely off the screen to avoid invoking any additional draw calls. Also note that these offscreen node objects are still physically present and still take up the same memory, even if they have visible set to NO. Furthermore, if these nodes are already running some animations/actions, they will continue updating outside the screen, unless you unschedule them.
Check this posts from the official cocos2d forum
is rendering invisible sprites in spritebatchnode cheap?
Performance Difference between visible = no and removeChild
Bad perfomance - many sprites with the same texture
Also, you can test it by yourself but i think that those post will help.
IMO setting visible=NO is enough, but depends on the sprite count.