Hello all,
As someone relatively new to Dolphin -- and a _total_ newbie to digging into Windows -- I have a question for you. I haven't found an answer to exactly this problem in the news archive, Wiki etc, but if it's there, a pointer is as good as a full answer for me. I have a view that requires redrawing following certain user input events. The view is put together mainly by a number of gif/jpeg pictures, which together cover the whole view. To reduce unwanted flicker, the contents of the view are first written to a bitmap through its canvas, before being copied onto the view. This, BTW, is done by overriding #refreshContents to self updateCachedBitmap ^super refreshContents -- and simply letting #onPaintRequired copy the cached bitmap onto the view. This works mostly as expected (and the slight delay from the user input to the update is no problem). The only small cosmetic problem left is that Dolphin -- or, most probably, Windows -- whites out the view before copying the bitmap onto it, causing a short "blink". I would very much like to eliminate this as well. Off the top of my head, I can think of two approaches for achieving this: 1) Calculate which parts of the view actually need updating, and performing some #invalidateRect: magic, or something. 2) Tricking Dolphin -- or, most probably, Windows -- into not erasing the view before the new one is copied from the bitmap. The first one should not be all that difficult, but requires a lot of work, especially if the look of the view is to be changed in the future. Thus, my question is: Is there a simple way to implement the second approach? Finally, I would like to congratulate OA on their product. The Value Edition, which I've purchased, is simply incredible VFM. -- Best regards, Ole Martin Halck |
Ole,
> Hello all, Welcome [snips] > Off the top of my head, I can think of two approaches for achieving this: > > 1) Calculate which parts of the view actually need updating, and > performing some #invalidateRect: magic, or something. > 2) Tricking Dolphin -- or, most probably, Windows -- into not erasing the > view before the new one is copied from the bitmap. > > The first one should not be all that difficult, but requires a lot of > work,especially if the look of the view is to be changed in the future. > Thus, my question is: Is there a simple way to implement the second > approach? Try replacing your #refreshContents with self updateCachedBitmap. self invalidateRect: nil erase: false This is just shortcutting the code your supersend executes but also tells Windows not to erase the screen first. However, as you say that you are redrawing the whole visible bitmap I'm not 100% if it will work. It's a simple experiment so worth a try though <g> Ian |
In reply to this post by Ole Martin Halck
Ole,
In addition to Ian's suggestion of telling Windows not to erase first, you might want to specify an rectangle to invalidate. It takes work both at the time that you detect a need to redraw (deciding on the rectangle to invalidate based on what has changed), and when handling the resulting paint messages. Look at the "paint struct" which you'll find available through PaintEvent. In #onPaintRequired:, you can find the invalid rectangle using an expression like this: aPaintEvent paintStruct rcPaint asRectangle Note that some of the WM_PAINT messages (which cause Dolphin to call your #onPaintRequired:) that your app will receive will result from a portion of the app's view being uncovered by other windows, and some will result from your explicit invalidate/update calls. In short, your paint method needs to look at the invalid rect and draw only what is necessary, but, you should expect to have to draw the whole thing at times. Perhaps the best reference to help you quickly understand Windows programming is Petzold's book from the Windows 3.1 era. The newer editions contain so much information that they risk overwhelming the reader. The older book holds up conceptually, though the details of how parameters are packed into messages have changed. Fortunately, Dolphin insulates you from much of the detail. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
"Bill Schwab" <[hidden email]> wrote in message
news:9l0bif$6s4vf$[hidden email]... > Ole, > > In addition to Ian's suggestion ... Thanks to both of you for swift replies. I suspect I will find this newsgroup extremely useful in the future. :-) > ...of telling Windows not to erase first, you > might want to specify an rectangle to invalidate. > [snip] Yes, this was what I meant with my approach #1. I'll look further into both suggestions. > Perhaps the best reference to help you quickly understand Windows > programming is Petzold's book from the Windows 3.1 era. The newer editions > contain so much information that they risk overwhelming the reader. The > older book holds up conceptually, though the details of how parameters are > packed into messages have changed. Fortunately, Dolphin insulates you from > much of the detail. Actually, I did a search for Windows programming stuff, and found what seems to be this book in Win95/98/NT version (bar some images etc which seem to have fallen out) on http://angelfire.com/al2/allf18/index.htm. Looks very useful. -- Ole Martin Halck |
In reply to this post by Ole Martin Halck
[hidden email] (Ole Martin Halck) wrote (abridged):
> 2) Tricking Dolphin -- or, most probably, Windows -- into not erasing > the view before the new one is copied from the bitmap. The erase is actually requested with a separate Windows message, namely WM_ERASEBKGND. I have found this apparently simple message to be one of the darker areas of Windows. It sometimes seemed to be send unexpectedly and not as part of a general WM_PAINT, eg when moving menus around. I hate it. I'd much rather have all painting routed through a single message. Little or nothing is gained by separating out erasure, in my experience. That's based on my Windows C++ experience, where I usually define WM_ERASEBKGND to return without erasing anything. I don't know Dolphin UI stuff well, but I see there is message #wmEraseBkGnd:wParam:lParam: which forwards to #onEraseRequired:. I suggest you override #onEraseRequired: to do nothing. I suspect you need to return true (or perhaps 1) to prevent the erasure being done by Windows. This is your simple solution. > 1) Calculate which parts of the view actually need updating, and > performing some #invalidateRect: magic, or something. That won't help the flicker. It'll just confine it to a smaller area. If you invalidate explicitly you can also tell Windows that the background doesn't need to be erased for that particular invalidation, but in general there may be invalidation requests sent from outside your control so it is not a complete solution. Also, in truth, you do want the background to be erased - it's just that you can combine erasure with drawing. I have found it best not to lie to my computer. Dave Harris, Nottingham, UK | "Weave a circle round him thrice, [hidden email] | And close your eyes with holy dread, | For he on honey dew hath fed http://www.bhresearch.co.uk/ | And drunk the milk of Paradise." |
In reply to this post by Ole Martin Halck
Ole,
I haven't had time to fully read through your post (I'm moving house today so am in a bit of a rush) but you might be interested in this DoubleBufferedView class which we've used successfully to represent animated views without flicker. In particular, implementing #onEraseRequired: to answer true (without doing any actual erasing) is the way we avoided the unpleasant flickering due to Windows automatically erasing the view by default. You may like to think about using the class itself since it is likely that this will appear in a future version of Dolphin. Best Regards, Andy Bower Dolphin Support http://www.object-arts.com --- Are you trying too hard? http://www.object-arts.com/Relax.htm --- --------------------------------------------------------------------- "Filed out from Dolphin Smalltalk 2000 release 4.0"! View subclass: #DoubleBufferedView instanceVariableNames: 'backSurface requiresRender' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! DoubleBufferedView comment: ''! DoubleBufferedView guid: (GUID fromString: '{F0757E5D-E398-4EDE-9E0F-8C6CCB3699FC}')! !DoubleBufferedView categoriesForClass!Unclassified! ! !DoubleBufferedView methodsFor! canvas "Answer a <Canvas> onto the back surface" ^backSurface canvas setBkColor: self backcolor; yourself ! flip "Private - Flip the current back surface to the front and paint it" backSurface drawOn: super canvas at: 0@0 extent: backSurface extent. ! initialize "Private - Initialise the receiver." super initialize. backcolor := Color white. requiresRender := false. ! initializeSurfacesFor: aPointExtent "Private - Initialize the front and back surfaces for a view size of aPointExtent" | canvas | backSurface notNil ifTrue: [ backSurface free ]. canvas := super canvas. backSurface := Bitmap compatible: canvas extent: aPointExtent. self invalidate. ! invalidate "Flag the current rendition as being invalid. A repaint will cause a render to occur" requiresRender := true. super invalidate! onCreated: anEvent "Private - Handler for view created " super onCreated: anEvent. self initializeSurfacesFor: self extent. ! onEraseRequired: aColorEvent "Private - Handler for erase background" ^true! onPaintRequired: aPaintEvent "Private - Handler for paint event" requiresRender ifTrue: [ self render ]. self flip. ! onPositionChanged: aPositionEvent "Private - Handle a window position change event (move or resize)." aPositionEvent isResize ifTrue: [ self initializeSurfacesFor: aPositionEvent extent. self repaint ]. ^super onPositionChanged: aPositionEvent! refreshContents "The model held by the receiver has been changed so repaint" self repaint ! render "Private - Render the background image" requiresRender := false ! repaint "Repaints the receiver" self render flip ! ! !DoubleBufferedView categoriesFor: #canvas!accessing!public! ! !DoubleBufferedView categoriesFor: #flip!operations!private! ! !DoubleBufferedView categoriesFor: #initialize!initializing!private! ! !DoubleBufferedView categoriesFor: #initializeSurfacesFor:!initializing!private! ! !DoubleBufferedView categoriesFor: #invalidate!operations!public! ! !DoubleBufferedView categoriesFor: #onCreated:!event handling!private! ! !DoubleBufferedView categoriesFor: #onEraseRequired:!event handling!private! ! !DoubleBufferedView categoriesFor: #onPaintRequired:!event handling!private! ! !DoubleBufferedView categoriesFor: #onPositionChanged:!event handling!private! ! !DoubleBufferedView categoriesFor: #refreshContents!public!updating! ! !DoubleBufferedView categoriesFor: #render!operations!private! ! !DoubleBufferedView categoriesFor: #repaint!operations!public! ! --------------------------------------------------------------------- "Ole Martin Halck" <[hidden email]> wrote in message news:9l077r$i1l$[hidden email]... > Hello all, > > As someone relatively new to Dolphin -- and a _total_ newbie to digging into > Windows -- I have a question for you. I haven't found an answer to exactly > this problem in the news archive, Wiki etc, but if it's there, a pointer is > as good as a full answer for me. > > I have a view that requires redrawing following certain user input events. > The view is put together mainly by a number of gif/jpeg pictures, which > together cover the whole view. To reduce unwanted flicker, the contents of > the view are first written to a bitmap through its canvas, before being > copied onto the view. This, BTW, is done by overriding #refreshContents to > > self updateCachedBitmap > ^super refreshContents > > -- and simply letting #onPaintRequired copy the cached bitmap onto the > > This works mostly as expected (and the slight delay from the user input to > the update is no problem). The only small cosmetic problem left is that > Dolphin -- or, most probably, Windows -- whites out the view before copying > the bitmap onto it, causing a short "blink". I would very much like to > eliminate this as well. > > Off the top of my head, I can think of two approaches for achieving this: > > 1) Calculate which parts of the view actually need updating, and performing > some #invalidateRect: magic, or something. > 2) Tricking Dolphin -- or, most probably, Windows -- into not erasing the > view before the new one is copied from the bitmap. > > The first one should not be all that difficult, but requires a lot of work, > especially if the look of the view is to be changed in the future. Thus, my > question is: Is there a simple way to implement the second approach? > > Finally, I would like to congratulate OA on their product. The Value > Edition, which I've purchased, is simply incredible VFM. > > -- > Best regards, > Ole Martin Halck > > |
Andy,
Interesting! One question/caution would be that without attention to doing the minimal drawing required, this might lead to a system that consumes a lot of CPU time, or put another way, something that "is slow but doesn't look it". It all depends on what the app does. If it's something in which all changes occur in a particular region, then optimized painting makes sense and is probably preferred. If it's something that can change anywhere/everywhere, then hiding the drawing process and tossing up a finished frame when its ready is the way to go. All that aside, I'll be interested to see what can be done with this class! Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Free forum by Nabble | Edit this page |