Link to home
Start Free TrialLog in
Avatar of gil_mo
gil_mo

asked on

Implementing an eraser pen?

I'm drawing a curve that is rapidly changing on a background. One method to update the curve is to redraw the whole background and to draw the new curve, but this is fairly slow since the background bitmap is big.

Is there a way I could create an erasing pen that erases the curve by simply drawing over it? The pen would somehow be created using the background bitmap... OR, any faster method to perform this task?
Note, the curve pen is more than 1 pixel thick, and the background bitmap could be any picture, not just a simple filled rectangle.
Avatar of nietod
nietod

If the bacgkground is not a solid color, and it sounds like it isn't, then there is no way to use a simple drawing tool (pen or brush) for this.

However, if you draw the curve using a XOR mode, which is common when "mouse tracking" you can erase the curve again very quickly by redrawing it a 2nd time.   I can elaborate if this is a good posibliity for you.

The other option is to copy the bakground over the curve, but to do this only for the rectanglular section the curve occupies, this will be faster than copying the entire bitmap.
FYI The XOR method is what drawing packages usually use to show objects being drawn in different sizes as the mouse is moved and it is the method windows uses to show a window being moved or resized (unless you have the "drag full windows" option selected).
Avatar of gil_mo

ASKER

nietod,
The XOR method cannot be taken into account for aesthetical reasons; the curve has to be a solid, unicolor pen. the bounding rectangle method requires to calculate the rectangle for every curve and in most cases the rectangle will be just about the whole background's size.

Maybe there is some way in which the bitmap could be 'polylined' back onto the curve, thus erasing it?
Your next best bet is to try to find a series of rectangles that cover the curve but that don't fill the entire area and bitblt over those.  The ideal rectangle is one that is long and thin.  (long in one direction, but not in the other.)  If you know the points that make up the curve, you can start at one end and add points to the rectangle and wait until one dimension exceeds a criticla value, like 8.  Then bitblt the rectangle and continue from that point.  You could also copy individual pixels from the background over the curve, that is easier, but likely to be noticabely slower.  (BitBt() is slow, but it is faster than copy each pixel one-at-a-time.)

Do you know the points in the curve?
Your next best bet is to try to find a series of rectangles that cover the curve but that don't fill the entire area and bitblt over those.  The ideal rectangle is one that is long and thin.  (long in one direction, but not in the other.)  If you know the points that make up the curve, you can start at one end and add points to the rectangle and wait until one dimension exceeds a criticla value, like 8.  Then bitblt the rectangle and continue from that point.  You could also copy individual pixels from the background over the curve, that is easier, but likely to be noticabely slower.  (BitBt() is slow, but it is faster than copy each pixel one-at-a-time.)

Do you know the points in the curve?
Your next best bet is to try to find a series of rectangles that cover the curve but that don't fill the entire area and bitblt over those.  The ideal rectangle is one that is long and thin.  (long in one direction, but not in the other.)  If you know the points that make up the curve, you can start at one end and add points to the rectangle and wait until one dimension exceeds a criticla value, like 8.  Then bitblt the rectangle and continue from that point.  You could also copy individual pixels from the background over the curve, that is easier, but likely to be noticabely slower.  (BitBt() is slow, but it is faster than copy each pixel one-at-a-time.)

Do you know the points in the curve?
ASKER CERTIFIED SOLUTION
Avatar of Zoppo
Zoppo
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Maybe.  Its possible that it would be faster to do the BitBlt without that clipping region.  i.e. bitblt the whole thing with no clipping.  (I'm not sure this is true, you'd have to test, but windows is pretty bad in its handling of non-rectangular regions.)
This is an ideal application for double-buffering. Just select your background bitmap into a memory DC, then draw the curve on it, then blit it all to the screen each time you need to update.

..B ekiM
even faster, only blt the section of the window that has changed.
Avatar of gil_mo

ASKER

Mike,
It's *that* blitting of the whole bkg from the mem DC to the screen DC that takes time. The curve is updated upon WM_MOUSEMOVE, for example, or upon WM_TIMER every 20 milliseconds.
That is why you only need to blt the part that has changed.  You can determine the area that is dirty by calling ClipBox.

Also are you currently double-buffering the drawing (ie. bitblt background to another bitmap, then draw onto the bitmap and blt that onto the screen).  This is usually much smoother.

I assume you aren't doing something silly like re-reading the bitmap from a file every loop.

Also are you sure it is the background blt that is taking the time and not the curve?

If you want an eraser, then create another (monchrome) bitmap and draw into that, then use it as a mask.  You can then use bitblting to copy only those pixels that were in the curve back to the screen (ie rubbing it out).
Avatar of gil_mo

ASKER

Adjusted points to 75
Avatar of gil_mo

ASKER

RONSLOW,
Can you please elaborate on your suggestion using a monochrome mask? I tried figuring it out but it seems just as time consuming...

"blt the part that has changed": this part is mostly very close to being the whole background...
Making the monochrome maks involves creating a monchrome bitmap of the same size as the background one and drawing your curve onto it.

Under Win2000/NT, you can then use MaskBlt to blt from ont bitmap to another onle where the mask is set.

However, unless you have WinNT/2000, you cannot use MaskBlt and have to emulate it with multiple BitBlt calls.  And to avoid flashing you'd need to do it offscreen.  So for Win95/98, this is probably going to be slower that simply redrawing the background and repainting the curve - especially if you do that offscreen anyway - its just two BitBlt's.


Avatar of gil_mo

ASKER

Adjusted points to 100
Avatar of gil_mo

ASKER

It seems like Zoppo's suggestion might be suitable, if there were a way to blit the bitmap into the region (NOT the bounding rect of the region). Is this possible? I'll add 50 pts + A to the supplier of a correct code snippet.
gil_mo> or upon WM_TIMER every 20 milliseconds.

How big is the rectangle?

..B ekiM
Zoppo seems to be suggesting the same as Nietod.

To bitblt into the region you'd need to either set up a mask and use that (unless you know it will be WinNT/2000 that's a lot of blts), or set up a clipping region (which probably won't help much anyway as the same amount of blting work would still be happening AFAIK).

I still think that blt and draw offscreen is the way to go.  Especially as you said that the curve's bounding rect will almst always be the entire window.

Avatar of gil_mo

ASKER

Mike,
Ok, the timer is set to about 50 mSec. The rectangle could be as big as about 350x220 .

RONSLOW,
Drawing offscreen will eventually involve the blitting of the whole rect onto the screen. This will only save the time difference between drawing the curve offscreen and drawing the curve onscreen.
> The rectangle could be as big as about 350x220

Then I still think double-buffering is your best bet.


To initialize:
1) Create a bitmap of the background.

To draw:
1) Create a memory DC
2) Select the bitmap
3) Draw on it
4) Blit it over

..B ekiM
qil_mo:
using an offscreen buffer does involve an extra blt .. but is much smoother.  Otherwise you see the curve flashing and drawing etc.  not very pretty.
Avatar of gil_mo

ASKER

RONSLOW,
The smoothness was never the issue in this question, but time. My initial implementation was using an offscreen blitting - the time consumption is still large.

Mike,
Comparing your suggestion to the onscreen option, we get:

Having Onscreen and offscreen begin with a DC and a memDC:

1. Onscreen: selecting the bitmap, copying it into DC.
   Offscreen: selecting bitmap, copying it into memDC.

2. Onscreen: drawing a curve on the DC.
   Offscreen: drawing a curve on memDC.

3. Onscreen: Nothing.
   Offscreen: Blitting from memDC to DC.

Total: Onscreen: Blitting bitmap to DC, drawing curve on DC.
       Offscreen: Blitting bitmap to memDC, drawing curve on memDC, blitting bitmap to DC.

As you can see, the *only* time gain we get using the offscreen implementation is the difference between drawing the curve onscreen and offscreen, as I had written before (this is questionable though, since in the offscreen implementation there's an additional blitting into memDC but never mind that.)

Mike, the time consumption is beyond the off/onscreen solution, as the massive blitting (offscreen step 3 above) is a big time consumer. If there is no way to blit *only* the path that was drawn (curve), maybe there simply *isn't* any solution?
:(
You've forgotten a step:

4: Onscreen: work up some complex and expensive algorithm to erase the line

   Offscreen: absolutely nothing!


I've offered you all the help I can.  Good luck with your project.

..B ekiM
Avatar of gil_mo

ASKER

NO Mike, this is covered in step 1: blitting a fresh copy of the bitmap onto the DC.

And as I said, I was using the offscreen method prior to this question (I always do). The time consumption is still in there.

Thanks for your help!!

Gil.
gil_mo, drawing operations that go on off screen are often much faster than the same drawing operations performed on-sreen.  (It depends on lots of hardware-dependant factors though.)  So you can't just compare the number of steps, you need to look at the fact that some of the steps may go much faster.  For example, in your case if steps 1 and 2 are enough faster in memory they can save time even though you have the extra step 3.
Avatar of gil_mo

ASKER

Please nietod, step 3 offscreen is exactly the same as step 1 onscreen.
Please read through my March 07 2000 - 09:42PM comment carefully.
How is the curve being defined?  If you know the mathematics behind it, you can update a couple of rectangles narrow that intersect the curve, like I suggested before.
Avatar of gil_mo

ASKER

Adjusted points to 200
Avatar of gil_mo

ASKER

The curve is a very wavy one. Leave this method. As I said before, the BeginPath method seems like a good start, but how do I continue from there?
From zoppo

Create a path from the curve using BeginPath()/EndPath().
Create a region from that path using PathToRegion()
Get the bounding box of the region with GetRgnBox()

Select the region as clipping region and bitblt the bounding box from the bitmap to screen.

But that might not be very fast, but it is worth a try.  Do you have questions about any of those steps?   Creating the path might be the biggest challenge there.  how are you drawing the curve?
Avatar of gil_mo

ASKER

From: gil_mo
"It seems like Zoppo's suggestion might be suitable, if there were a way to blit the bitmap into the region (NOT the bounding rect of the region)."

As I already wrote, the bounding rectangle of the curve will be always close to the whole bitmap.  

Curve is drawn by Polyline(). So first two steps can be done easily.
>> if there were a way to blit the bitmap into the
>> region (NOT the bounding rect of the region)."

BitBlt() must be passed rectangular sections to copy, but what is actually copied will be limited by the destination's clipping region.  Zoppo is hoping that by specifying a small enough clipping region the BitBlt() will work faster as it will have less points to copy. (It doesn't actually copy the portions that are outside the clipping region even if they are in the rectangle you specify.  So this might be faster.  Although the region calculations might eat up the savings and then some, but it is worth trying.  I would be pretty confident that if you specify a huge rectangle to copy and have a small rectangular clipping region, the copy would go at about the speed as if you had just copied the small rectangle.  The question is what will happen when the clipping region is not a simple rectangle?
Avatar of gil_mo

ASKER

Actually there is no need to convert to a region. SelectClipPath can be used for this matter.

Nietod, thanks for clarifying Zoppo's answer.

Zoppo, I haven't tried it yet to see if it works, but the idea is brilliant.