Link to home
Start Free TrialLog in
Avatar of zebada
zebada

asked on

Draw/Undraw line

I have a bitmap image on which I need to draw a single straight line of arbitrary width and of solid color (i.e. not using XOR) and then erase the line by restoring the original pixels from the bitmap image.

Currently I am using CopyRect to restore the pixels but it is way slower than using XOR to draw/undraw the line.

Can anyone give me a method that is as fast as XOR but allows me to choose a color to draw the line?
Avatar of bluecomet
bluecomet

Hi,

what you can do is to save the coordinate of the line in  temporary variables. Say that we have a line that begin at X:30 and Y:40 and end at X:50 and Y:40 with a balck color. Save the coordinate and the color and you draw the line in another color. Then when you will return the last color you can do it easily.

What you can do also is to define a structure for all figure(line, rectangle, circle...) when you have many of them. Then put it in an array and you do all what you want with that and it isn't dificult to handle. The same you can do for pixels: make a structure, save the old value and then when you will do an undo you can restore the value rapidly. Sure you will ask me, does it will be fast? I answer you it depend of the quantity of your figure or pixels. More (than 700000) you will have, more it will be slower.

It's another way to think about your problem.

Hope that will help you


$Regards.
Hi zebada,

this a simple exmaple how to draw and undraw a line

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
// Fill the Image with the Red Color
procedure TForm1.FormCreate(Sender: TObject);
begin
  With Image1.Canvas Do
  Begin
    Brush.Color := clRed;
    FillRect ( Rect(0,0,Image1.Width,Image1.height));
  End;
end;

// Draw and Undraw a Line
procedure TForm1.Button1Click(Sender: TObject);
begin
  With Image1.Canvas Do
  Begin
    Pen.Mode := pmnotXor;
    MoveTo ( 0,0 );
    LineTo ( Image1.Width,Image1.Height);
  End;
end;

Best Regards

Cesario
Avatar of kretzschmar
why not use a simple other object, like tshape, where you draw your line on (rest is transparent) and the object self is in front of your image.

to clear the line just adjust its visibility.

the depending postion calculations are the same as for drawing the line also as to calc the rect.

just an idea

meikl ;-)
Avatar of zebada

ASKER

Bluecommet, I don't have a plain background color to restore - the background is an arbitrary bitmap. Unless you're suggesting that I write my own line drawing routines and save the pixels prior to drawing my line then put them back afterwards - That's way too sloooow.

Cesario - I explicitly asked for a way to do it WITHOUT using XOR. XOR is no use when trying to draw a solid color line on an arbitrary background.

Kretzschmar - I'll try your suggestion - I've not ever used TShape - I guess its time to read the doco.
similar to kretzschmar ... I used a TImage that I created at runtime. Set the TImage.parent to the Image you want the line to be. the draw a line to the TImage and set it to tranparent := true; you can now set the left and top so it fits. When you want to delete the line just free the TImage or set visible := false; Worked with me (not for lines but it worked)
I can sugest two methods

1) Use lineDDA to draw & erase the line(s).  When drawing you'll need to keep a record of all the obscured pixels, their co-ordinates & color info.  When erasing your just plugging those bits 'o data backinto the bmp.

2)If you're drawing many lines of the _same_ color you could try doing something like this:

a)keep a separte bmp for the lines.  When you want to add a line draw it in black (the rest of the bmp should be white).
b)when drawing the lines to the bmp it'll take a couple of bitblts
 i)one bitblt (anding the background & the line's bmp) to get the background with black lines
 ii) one bitblt (anding the lines with a bmp filled with your line color) to get colored lines
 iii) a final bitblt (or'ing the results of i & ii) to combine background & colored lines.

the only advantage of 2) is that the worst case is the same no matter how many lines you need - thoug the best case isn't so hot as you've got 3 for bmp bitblt's.


GL
Mike
Avatar of zebada

ASKER

Kretschmer, MarcG
Using a TShape/TImage overlay was too slow. I did some experiments and when I draw lines using LineTo on a TGraphicObject canvas it will draw an order of ten slower if the TGraphicObject overlaps any other control. I guess under the hood Delphi needs to blit one or other of the bitmaps to the screen to keep the display accurate.

Mike,
I think option 1 is too slow.

Option 2:
Good idea but I've tried keeping a copy of the background and copying the smallest portion of the background bitmap that covers the line but it's still too slow - order of ten times slower.

The reason I need speed is I am drawing analogue dial gauges like the speedometer in your car (up to 25 on the screen simultaneously) and they each need to be updated anywhere up to 25 times per second.

Currently I am just using a black background (and drawing using XOR) but I want to change the dials to allow a custom bitmap to be used as the dial's background.

Any other suggestions - even if it sounds crazy - it might give me an idea.

Remember it has to be as fast as possible.

Cheers
Paul
Zebada, there is no other solution than:
1. Redraw the background
2. Draw your line(s)

But there are many way to speed up the background copy:
1. Use 2 internal TBitmap (NOT TImage). The first one contains your background. On the second, copy the background, then draw your line. Copy the second bitmap to the TImage.Picture.Bitmap.
2. Bypass the CopyRect using BitBlt api (Get the DCs from your TBitmap.Handle)
3. Change the depth of your bitmaps (set TBitmap.PixelFormat to pf8bit if possible, it a lot faster)
4. The faster but the harder: Use DirectDraw surfaces (DirectX) to draw all, then blit the result to your screen! (That's what I do to draw vectorial maps!)
"4. The faster but the harder: Use DirectDraw surfaces (DirectX) to draw all, then blit the result to
your screen! (That's what I do to draw vectorial maps!)"

I thought about this, but for 2d drawing directX surfaces aren't going to be any faster.  The only thing, in 2d, that get's accelerated is flipping, or blitting a whole surface.  So if your bottleneck is drawing the line, you're still out of luck.

zebada, perhaps you could show us an example so we could see more clearly what we're trying to optomise?

GL
Mike
Avatar of zebada

ASKER

bkg97, I didn't know about bitblt - any more info?

Mike, I'll post the code I am working with at http://www.blacky.co.nz/free/analog.zip
It won't be there for maybe 24 hours 'cause I'm stuck in Singapore - won't be home till tomorrow. Hence the slow response to the comments. Networks are hard to find when you're roaming.

Post another comment here if you can't download the zip file after about 24 hours.

Cheers
Paul
ASKER CERTIFIED SOLUTION
Avatar of bkg97
bkg97

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
Avatar of zebada

ASKER

bkg97, Thanks for the code segment however I can't seem to see any speed difference in using BiBlt/CopyRect.

It still takes me about 2.6 seconds to to 1000 gauge refreshes. If I could get that closer to your 160ms I would be extremely happy.

I've posted the sample code I am working on at:
http://www.blacky.co.nz/free/analog.zip

Now here's the strange thing:

When using XOR (i.e. no bitmap blitting at all) I can draw 1000 lines in about 160ms. (Pentium III 500Mhz)

When NOT using XOR (i.e. using bitmap blitting) the time blows out to about 2.6 seconds. A factor of about 16 times slower.

Another really strange thing is that it is MUCH FASTER with a line width of >1 than with a line width of 1. Why should that be?

Lastly there is aproblem with my code that causes the needle on the gauge to disappear whenever the gauge is repainted and the position of the needle has not changed. Can anyone solve that also? (for more points).

Cheers
Paul
Avatar of zebada

ASKER

By the way
Feel free to make comments if you can see where any other improvements could be made in the code - I won't be offended.
Cheers
Paul
Wow! 2.6 seconds! Even the worst line drawing method could make better.
Well, I'm downloading your project and tonight I'll take a look at.

Thierry.
Avatar of zebada

ASKER

Thanks to bkg97 and Mike (edey) I have now got a FAST working gauge.

200 points for bkg97 and 200 points for Mike. See new question "Points for Mike (edey)"

If anyone is interested in the "final" code post a message here and I'll zip it up and post it to my web site.

Thanks for your help
Paul