Board index » delphi » Problems drawing on a Canvas

Problems drawing on a Canvas

Hi,

I'm having problems drawing on a canvas with my raytracer. the basic
idea is as follows:

The Main app form opens a modal form called TraceFrm.

TraceFrm contains a scroll bar and an edit to inform the user of the
trace progress.

When the form is shown, it sets up it's progress bar, opens another
form called OutputFrm (on which I have an Image control), and starts a
separate thread to do the actual raytracing ( called TraceThread).

When OutputFrm is opened it contains an image control (called image1).
It assigns a bitmap of the appropriate size to the graphic property.

During the raytrace, the trace thread uses the synchronize method to
alter the pixels property of the canvas property of the image1
control.

Then when the raytace finishes, OutputFrm.Image1.Picture is either
saved to file or assigned to the clipboard.

There's are just two small snags.....

1.  When I'm just writing to a targa file, and not using any of the
above code, everything runs nice and quick. When i try to write to the
canvas of the image control, the program runs like a particularly
tired snail.

2. Nothing ever actually appears in the image control!!!

Any ideas???

Relevant source below

PS. I hope I'm not asking a whole load of FAQ's here......

unit TraceForm;

interface

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

type
  TTraceFrm = class(TForm)

{SNIP}

  end;

var
  TraceFrm: TTraceFrm;
  Tracing:boolean;

implementation

uses TraceExceptions, TraceThrd, OutputForm,ClipBrd, TraceMgr;

{$R *.DFM}

var
   MyThread:TraceThread;

{SNIP}

procedure TTraceFrm.FormShow(Sender: TObject);
begin

{SNIP}

if ViewTrace then OutputFrm.WindowState:=wsNormal else
OutputFrm.WindowState:=wsMinimized;

if ViewTrace or BitmapOut or ClipboardOut then OutputFrm.Show;

{SNIP}

end;

procedure TTraceFrm.Finished(Sender:TObject);

begin
Tracing:=false;
if BitmapOut then OutputFrm.Image1.Picture.SaveToFile(BitmapOutFile);
if ClipboardOut then Clipboard.Assign(OutputFrm.Image1.Picture);
if ViewTrace or BitmapOut or ClipboardOut then OutputFrm.Close;
Close;
end;

initialization
Tracing:=false;
end.

unit OutputForm;

interface

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

type
  TOutputFrm = class(TForm)
    ScrollBox1: TScrollBox;
    Image1: TImage;
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  OutputFrm: TOutputFrm;

implementation

uses DataMgr;

{$R *.DFM}

procedure TOutputFrm.FormShow(Sender: TObject);

var
   Bitmap:TBitmap;

begin
Bitmap:=TBitmap.Create;
Bitmap.Width:=Camera.ResX;
Bitmap.Height:=Camera.ResY;
Image1.Picture.Graphic:=Bitmap;
end;

procedure TOutputFrm.FormClose(Sender: TObject; var Action:
TCloseAction);
begin
{Image1.Picture.Graphic.Free;}
end;

end.

unit TraceThrd;

{Martin Harvey 11/12/96 }

interface

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

type
  TraceThread = class(TThread)
      constructor Create(PBar:TProgressBar;OpEdit:TEdit);
  private
    { Private declarations }
    Progbar:TProgressBar;
    OperationText:TEdit;
    procedure WriteOper1;
    procedure WriteOper2;
    procedure WriteOperLine;
    procedure SetMax;
    procedure SetPos;
    procedure WriteTargaError;
    procedure WriteTexMapError;
    procedure WriteBMPData;
  protected
    procedure Execute; override;
  end;

implementation

uses TraceMgr,DataMgr,Targa,VCMath,TraceExceptions,OutputForm;

type
    LineData=array[0..2047] of TColour;

var
   GlobalXpos,GlobalYpos:integer;
   BMPData:TColor;

{ A VERY BIG SNIP}

procedure TraceThread.WriteBMPData;

begin;
OutputFrm.Image1.Canvas.Pixels[GlobalXpos,GlobalYpos]:=BMPData;
end;

procedure TraceThread.Execute;

var
   OutputTarga:TTargaFile;
   TempLine,LastLine,ThisLine:^LineData;
   PlaneUp,PlaneRight,ViewDir,RayDir,UpDir,
   RaySweepY,RaySweepX:TVector;
   Xpos,Ypos:integer;
   LongXpos,LongYPos,LongResX,LongResY:double;
   RayColour:TColour;
   RayData:TColData;
   Xwrite:integer;

begin

{SNIP}

try
 try

{SNIP}

   OutputFrm.Image1.Canvas.Pen.Style:=psSolid;

{SNIP}

   for Ypos:=0 to Camera.ResY do
   begin
        GlobalYpos:=Ypos;

{SNIP}

        for Xpos:=0 to Camera.ResX do
        begin
             if terminated then raise ETraceAbort.Create('Abort button
pressed');
             LongXpos:=Xpos;
             GlobalXpos:=Xpos;
             LongYpos:=Ypos;

{SNIP}

        if Ypos>0 then
        begin

{SNIP}

                  if TargaOut then OutputTarga.WritePix(RayData);
                  if ViewTrace or BitmapOut or ClipboardOut then
                  begin
                       BMPData:=FloatToWin(RayColour);
                       Synchronize(WriteBMPData);
                  end;
             end;
        end;
        TempLine:=LastLine;
        LastLine:=ThisLine;
        ThisLine:=TempLine;
   end;
finally

{SNIP}

end;
except

{SNIP}

end;
end;

end.

Martin Harvey

Uni email: mc...@cam.ac.uk
Home email: mc...@harvey27.demon.co.uk

Uni web pages: http://www-stu.pem.cam.ac.uk/~mch24/

Home web pages: shortly to appear on the demon server

 

Re:Problems drawing on a Canvas


Quote
mc...@harvey27.demon.co.uk (Martin Harvey) wrote:
>There's are just two small snags.....

>1.  When I'm just writing to a targa file, and not using any of the
>above code, everything runs nice and quick. When i try to write to the
>canvas of the image control, the program runs like a particularly
>tired snail.

Synchronizing threads has a huge performance cost.  You basically have
to halt the current thread and wait for the other thread to reach a
point where it's safe for the OS to switch execution to it.  (when the
receiving thread makes an API call to process messages)

Thread synchronization costs a minimum of a thousand clock cycles or
so, each way, and can run into the hundreds of thousands or even
millions of clock cycles depending on what the receiving thread is
doing.  You don't want to take that kind of detour for every pixel in
your image, unless calculating each pixel takes several seconds
anyway.  (And if you're using the ultra-slow Pixel[] property, it just
might!)

Besides, synchronizing your two threads defeats the whole purpose of
using threads.  On a single processor machine, the only time threads
provide a performance benefit is when most of the threads are
suspended waiting for some external event (such as file I/O).  It
sounds like both your threads are CPU-bound, which means - at best -
the computations in each thread can run no faster than if they were
executed serially in the same thread.  In reality, it means that
thread overhead will make the two operations take longer than if they
were executed serially in the same thread.  Add heavy thread
synchronization to the mix, and thread overhead will eat you alive.

I also find it hard to believe that anything that uses
GetPixel/SetPixel (which is what TBitmap.Pixels[] uses) could ever be
described as 'quick'.  <g>

Quote
>2. Nothing ever actually appears in the image control!!!

That might be because thread synchronization requires message
processing, and bitmap canvases are destroyed after VCL processes each
message.  Don't bother digging into this one - if you clear out the
thread mess, this will take care of itself.

Quote
>PS. I hope I'm not asking a whole load of FAQ's here......

Sort of.  If "Getting in over your head by using threads" isn't in the
FAQ, it should be.  <g>    Elegance is simplicity, and threads are
neither.

-Danny
==============
 Danny Thorpe
 author of "Delphi Component Design" 1996 Addison Wesley
 ISBN 0-201-46136-6
==============

Other Threads