-4

I want to speed up painting a bitmap, therefore I designed a class like BITMAP THREAD CLASS. Once the individual painting of a partial image is finished I want to merge all image in the Thread.done procedure My code goes like this

    type
      TbmpthreadForm = class(TForm)
        .....
        THreadImage: TImage;
        procedure Button_threadstartClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      private
        { Private-Deklarationen }
        procedure ThreadDone(Sender: TObject);
      public
        { Public-Deklarationen }
        fserver, fdatabasename, ftablename: String;
        global_thread_counter: Integer;
        XPixel, YPixel: Integer;
        Masterbitmap: TBitmap;
      end;

    var
      bmpthreadForm: TbmpthreadForm;

    implementation

    {$R *.dfm}

    procedure TbmpthreadForm.ThreadDone(Sender: TObject);
    begin
      dec(global_thread_counter);
      MyStatusBar.SimpleText := 'Thread Count ->' + IntToStr(global_thread_counter);

      Masterbitmap.Canvas.Lock;
      with (Sender as TPaintBitmapThread) do
      begin
        bitblt(Masterbitmap.Canvas.handle, 0, 0, XPixel, YPixel,
          bitmap.Canvas.handle, 0, 0, srcand);
        THreadImage.Picture.Bitmap.Assign(Masterbitmap);
        // lets see local tthread intermediate results  and save it to HD
        THreadImage.Picture.Bitmap.SaveToFile('c:\temp\myimage' + IntToStr(Index)
  + '.bmp');
      end;
      Masterbitmap.Canvas.UnLock;

      if (global_thread_counter = 0) then
      begin
         ...
      end;
    end;
procedure TbmpthreadForm.Button_threadstartClick(Sender: TObject);
var
     ..... 
begin

  index_max := 2000000;
  threadCounter := 10;
  Indexdelta := round(index_max / threadCounter);

  ///
  ///
  ....
  Masterbitmap.Width := XPixel;
  Masterbitmap.Height := YPixel;

  for i := 0 to threadCounter - 1 do
  begin
    n := i * Indexdelta;
    m := (i + 1) * Indexdelta;
    //  just a test sql string .... 
    sqlstr := 'select * from  Mytable  where objectindex <' + IntToStr(m) +
      ' and Objectindex >' + IntToStr(n);

    aPaintBitmapThread := TPaintBitmapThread.Create(XPixel, YPixel, ......   , fserver, fdatabasename, ftablename,
      sqlstr, i);
    aPaintBitmapThread.OnTerminate := ThreadDone;
    Memo1.Lines.Add('start thread->' + IntToStr(i));
    inc(global_thread_counter);
  end;

end;

The Thread.done design follows a previous topic here on SO ( reference question As the resulting image/Masterbitmap looks a bit different from run to run , I guess my approach is not thread safe design for copy Thread bmp content into the masterbitmap in the VCL mainform, I can not see any error in my code, what is wrong ????

additional question

Q1 : fbitmap inside TPaintBitmapThread is created inside the Thread.create procedure, for TAdoconnection I found the comment, it should be created inside the thread.execute. Must this also be done the the bitmap ?

Q2 : the attached image shows the expected result of an image(Bitmap) by an Thread and the actual image results (as seen by example of the painting error the THreadImage.Picture.Bitmap.SaveToFile command)

Community
  • 1
  • 1
user1769184
  • 1,571
  • 1
  • 19
  • 44
  • Tell us more about XPixel and YPixel. Why can't we have a complete program? – David Heffernan Jun 24 '14 at 13:28
  • the complete code is pretty long; Xpixel and ypixel is the size a bitmap inside the thread and I use the same size also for the masterbitmap. with my bitblt comand I just copy the local thread bmp into the master bmp – user1769184 Jun 24 '14 at 13:42
  • I don't want to see your complete code. I want to see a complete program that illustrates the issue. An SSCCE. – David Heffernan Jun 24 '14 at 13:57
  • SSCCE is pretty hard to create; may be with q1 I can get more help or the additional strange debug result , that Saved bitmap from individual thread also shows complete data / image. ( it sould only contain a certain amout of drawings); I added the debug line in the source code above – user1769184 Jun 24 '14 at 14:13
  • one more remark : Asynchronous threads drawing in Bitmaps Delphi, Answer by Remy Lebeau; Is the critical section also needed ? Did I undersatnd the solution here correctly? – user1769184 Jun 24 '14 at 14:22
  • Well, I cannot help you without you showing code and a detailed description of what you expect and how your code fails to meet expectations. Sorry. Maybe somebody else will. – David Heffernan Jun 24 '14 at 14:27
  • "*SSCCE is pretty hard to create*" Please realize it's even harder for us to guess the code which we cannot see. During your trials of making an SSCCE, I'm sure you'll find your problem yourself and wouldn't have to ask a question in the first place. Any kind of development like this needs a separate test app - something like this is bound for issues if you jump straight into developing the final app from the beginning. – Jerry Dodge Jun 24 '14 at 14:54
  • will do a new post here with the sscce code – user1769184 Jun 24 '14 at 18:37

1 Answers1

1
    bitblt(Masterbitmap.Canvas.handle, 0, 0, XPixel, YPixel,
      bitmap.Canvas.handle, 0, 0, srcand);

you explicitly called Masterbitmap.Canvas.Lock, however you didn't call bitmap.Canvas.Lock (so you can loose the canvas handle anytime within this call...)

Additionally, you need to consider thread safety within GDI itself: Sharing of any GDI objects between different threads should be avoided at all cost. For example if you select a bitmap into two different device contexts at the same time (but in different threads) you may run into problems within GDI itself...

Please note that older versions of delphi don't protect against sharing of cached handles (Font, Brush and Pen handles are cached within a global list. This was fixed in an XE3 service pack if I remember correctly).

In short: I'd consider to avoid TCanvas and TBitmap completely if you really need multi-threading. (It's much easier to be multi-threading safe that way)

Daniel
  • 36
  • 1
  • Font, Brush and Pen handles are cached within a global list. -> this seem to be a good input, even XE5 show the same error behaviour. ( see the updated question) – user1769184 Jun 24 '14 at 17:16
  • guess that this is the real root cause of my problem "Please note that older versions of delphi don't protect against sharing of cached handles (Font, Brush and Pen handles are cached within a global list. This was fixed in an XE3 service pack if I remember correctly)." – user1769184 Jun 25 '14 at 07:07