.NET Forum / Languages / C# / November 2006
Scrolling text that doesn't flicker
|
|
Thread rating:  |
pigeonrandle - 12 Nov 2006 22:03 GMT Hi, I have seen loads of different ways to do this, but the all seem to yield the same result - text that doesn't flicker when it's moving too slowly! Does anyone know 'the best way' to make text scroll...
eg Override OnPain and OnPaintBackground
Override WndProc
Use GDI
Use API
Or do i need to create some weird control in c++?!
Thanks in advance, James
Peter Duniho - 13 Nov 2006 00:08 GMT > Hi, > I have seen loads of different ways to do this, but the all seem to > yield the same result - text that doesn't flicker when it's moving too > slowly! "Flicker" is generally caused by a single problem, regardless of what you're drawing: erasing the previous image on-screen before drawing the new image.
Typically, this is addressed using an off-screen buffer to actually draw the image, which is then copied directly to the screen without erasing whatever was there previously. The reason you need to erase in the first place is to get rid of whatever was there previously, but if you are always copying a whole new bitmap (or portion of a bitmap) on top of every part of the display that needs changing, then no erasing is necessary, and no flicker occurs.
In .NET, I've been told one can enable "double-buffering" to address this. The term "double-buffering" isn't correct, IMHO (it more correctly refers to a related but different idea in which the entire screen buffer is switched back and forth), but knowing that that's the phrase some .NET people use may help you find the technique in the .NET documentation. I haven't used it myself, so I can't tell you the specifics.
Pete
Dave Sexton - 13 Nov 2006 00:40 GMT Hi James,
As Peter suggested, double buffering is an option to reduce flickering (the only one I'm aware of as well). In the constructor of a class that derives from Control (including the Form class) you can use the following code to enable double buffering for that control (1.* applications):
this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
In 2.0 apps, just set the Control.DoubleBuffered property to true (it uses the new ControlStyles.OptimizedDoubleBuffer flag internally).
 Signature Dave Sexton
> Hi, > I have seen loads of different ways to do this, but the all seem to [quoted text clipped - 15 lines] > Thanks in advance, > James Michael C - 13 Nov 2006 04:28 GMT > Hi James, > > As Peter suggested, double buffering is an option to reduce flickering > (the only one I'm aware of as well). The other option is to just draw everything once. If you are doing something like a chessboard this is easy, just draw each square onto the screen. If the chessboard has pieces you could use regions to draw the pieces and then draw the square around them. Unfortunately for text I don't think this is possible. You have to use double buffering because it's not possible to draw text with a background and it is not possible to find the region surrounding this text. This also applies to many of the features of GDI+ such as anti-aliasing and transparencies, it's no longer possible to draw in one go so double buffering is almost mandatory these days.
Michael
Dave Sexton - 13 Nov 2006 04:45 GMT Hi Michael,
But for scrolling text you can't draw everything only once. It's scrolling!
BTW, you can draw text with a background color in the 2.0 framework using the TextRenderer class, which I believe uses GDI, not GDI+.
 Signature Dave Sexton
>> Hi James, >> [quoted text clipped - 12 lines] > > Michael Michael C - 13 Nov 2006 04:58 GMT > Hi Michael, > > But for scrolling text you can't draw everything only once. It's > scrolling! Yes, you can (assuming you can draw the background with the text as you described). There will be x frames per second, for each frame if you draw each pixel on the surface only once then you don't need double buffering.
> BTW, you can draw text with a background color in the 2.0 framework using > the TextRenderer class, which I believe uses GDI, not GDI+. If that's the case then it would be possible to use the API directly in 1.1, although I don't recall there being an API that would let you draw text with a background.
Michael
Dave Sexton - 13 Nov 2006 05:06 GMT Hi Michael,
>> But for scrolling text you can't draw everything only once. It's >> scrolling! > > Yes, you can (assuming you can draw the background with the text as you > described). There will be x frames per second, for each frame if you draw > each pixel on the surface only once then you don't need double buffering. I really don't understand what you mean. How would that prevent Windows from clearing the render target before the next operation?
I always thought double-buffering worked like the swap chain in DirectX, where the back buffer is simply presented instead of the target being cleared. Without the buffer, I assumed Windows will clear the target automatically for each WM_PAINT message, causing a flicker.
Care to explain in more detail?
>> BTW, you can draw text with a background color in the 2.0 framework using >> the TextRenderer class, which I believe uses GDI, not GDI+. > > If that's the case then it would be possible to use the API directly in 1.1, > although I don't recall there being an API that would let you draw text with > a background. Yea, I'm not sure how they do it but it's there and it works (and it's GDI according to the MSDN docs :)
 Signature Dave Sexton
Peter Duniho - 13 Nov 2006 05:28 GMT >>> But for scrolling text you can't draw everything only once. It's >>> scrolling! [quoted text clipped - 5 lines] > I really don't understand what you mean. How would that prevent Windows > from clearing the render target before the next operation? He's saying that, if you had a way to ensure that each pixel on the screen that has changed is drawn only once, setting it to the exact desired new value, you can, once per frame, draw everything only once.
Obviously something animated has to be drawn multiple times. But the flicker doesn't result from that. It results from drawing things multiple times *per frame*. That's what he means to avoid.
In theory, it's even possible to do so (see my other post, about using regions to mask the text area while filling the remaining background). In reality, that's a) a much more complicated solution than using an off-screen buffer, and b) may not even perform as well (since it would rely on using regions to mask the text area while the background is being drawn).
> I always thought double-buffering worked like the swap chain in DirectX, > where the back buffer is simply presented instead of the target being > cleared. Without the buffer, I assumed Windows will clear the target > automatically for each WM_PAINT message, causing a flicker. > > Care to explain in more detail? This is why I dislike the use of the term "double buffering" to describe this. It's similar to, but not exactly the same as, the existing double-buffering (or even triple-buffering, as is sometimes the case) used in DirectDraw. In DirectDraw, double-buffering means there are two entire frame buffers. One is always being used by the video card's RAMDAC to display an image on the monitor, and the other is the one being drawn to at that given moment. Once drawing has been completed, the entire active frame buffer is switched (flipped), and the process starts anew.
This is kind of what the .NET "double buffering" is doing, except that it's not a low-level hardware thing. There's an alternate, off-screen buffer and rather than flipping the actively displayed buffer, the off-screen buffer is simply copied to the active on-screen buffer. I don't like using the same term for both, because it confuses the issue.
By doing this, any pixel that changes on the screen is drawn exactly once for a given change (frame). Which is what needs to happen to avoid flicker. And it certainly can happen that way, even as the text is scrolling. The requirement is that the drawing happens once for each change, not that it only happen once period.
Pete
Dave Sexton - 13 Nov 2006 05:39 GMT Hi Peter,
Great explanation, thanks.
(You're not going to start billing me, right? ;)
 Signature Dave Sexton
>>>> But for scrolling text you can't draw everything only once. It's >>>> scrolling! [quoted text clipped - 49 lines] > > Pete Peter Duniho - 13 Nov 2006 05:50 GMT > Hi Peter, > > Great explanation, thanks. You're welcome, of course.
> (You're not going to start billing me, right? ;) No way...if I started charging for this kind of stuff, I'd have to work a lot harder to make sure I knew what I was writing about. :)
Pete
Dave Sexton - 13 Nov 2006 06:10 GMT Hi Peter,
Work a lot harder?
I'm not sure why you're even working at all. You could be completely making stuff up on some of these topics and I wouldn't be the wiser.
And you'd still get the credit ;)
 Signature Dave Sexton
>> Hi Peter, >> [quoted text clipped - 8 lines] > > Pete Peter Duniho - 13 Nov 2006 18:05 GMT > Hi Peter, > [quoted text clipped - 4 lines] > > And you'd still get the credit ;) Now you're catching on... :)
Michael C - 13 Nov 2006 05:07 GMT > But for scrolling text you can't draw everything only once. It's > scrolling! The other thing you can do with scrolling text is use the ScrollWindow API to move the contents of the window towards the left and then fill in the invalid areas.
Michael
Peter Duniho - 13 Nov 2006 05:27 GMT > The other thing you can do with scrolling text is use the ScrollWindow API > to move the contents of the window towards the left and then fill in the > invalid areas. But those invalidated areas are cleared by the scrolling (or need to be cleared when you fill them in), so that portion will still flicker.
Michael C - 13 Nov 2006 07:31 GMT > But those invalidated areas are cleared by the scrolling (or need to be > cleared when you fill them in), so that portion will still flicker. That's true, I guess all the same issues of double buffering or drawing with a background would apply to those areas. This could still be useful as it really speeds up scrolling, although could also complicate the issue.
Peter Duniho - 13 Nov 2006 18:08 GMT >> But those invalidated areas are cleared by the scrolling (or need to be >> cleared when you fill them in), so that portion will still flicker. [quoted text clipped - 3 lines] > as it really speeds up scrolling, although could also complicate the > issue. It does speed up scrolling quite a lot (especially on large areas), and IMHO is the appropriate method any time you simply need to scroll something on the screen. I was just pointing out that it doesn't avoid other aspects of the problem.
That said, because the flickering region is so small when using that technique, the flicker is MUCH less noticable. In some cases, it might be an appropriate solution...not because it eliminates flicker altogether, but just because the user can barely notice what flicker does exist.
It really just depends on the particular situation. Funny how that keeps coming up in these programming topics. :)
Pete
Peter Duniho - 13 Nov 2006 05:14 GMT > Hi Michael, > > But for scrolling text you can't draw everything only once. It's > scrolling! As Michael notes, in theory as long as you ensure that every pixel that needs to change is drawn, you can avoid the flickering.
However...it's MUCH harder to accomplish this, and in some cases may be impossible of course. Even with the original GDI, while it might have been theoretically possible to draw the text, and fill the region outside the text with the background color, that would have been such an expensive drawing operation, no one sensible person would have done it that way.
The most common solution would have been to simply maintain a bitmap the same size as the on-screen window, drawing into that and then blting the results to the screen. Another solution would be to use smaller bitmaps, the size of whatever it is you want to draw, blt those as they are drawn to the appropriate spot on the screen. Of course, as was noted in Michael's earlier post, if the kinds of things that are being drawn lend themselves to simply copy an entire image to fill a specific area on the screen, then that works too.
The key in all solutions is to avoid first clearing an area on the screen and then drawing something else on top of that.
> BTW, you can draw text with a background color in the 2.0 framework using > the TextRenderer class, which I believe uses GDI, not GDI+. It's been awhile, but my recollection is that this doesn't make a difference. That is, when drawing text with a background color (using the opaque style), GDI would first fill in the background and then draw the text, all directly to the destination DC. It didn't avoid flickering...it just was more convenient than calling FillRect directly yourself (and also had the advantage that you didn't have to measure the text first, since the TextOut function handled the whole thing for you).
Even if using "double buffering" (sorry, I still hate that term as applied here) could be avoided and dealing with updating screen graphics could be solved some other way, IMHO there's not really any great reason to do so. Using an off-screen bitmap, as in "double buffering", is a reasonably elegant way to address a variety of complications that come up with any sort of animated or changing display, and with the amount of RAM available on most systems these days, doesn't incur much of a penalty.
Pete
Dave Sexton - 13 Nov 2006 05:46 GMT Hi Peter,
Thank you.
So is there some double-buffer implementation in WPF that uses the hardware?
(I heard that WPF uses DirectX)
 Signature Dave Sexton
>> Hi Michael, >> [quoted text clipped - 42 lines] > > Pete
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|