ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 TImageEnMIO cannot generate images correctly in multithreading
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

Flashcqxg

96 Posts

Posted - Jan 23 2024 :  18:23:47  Show Profile  Reply
Using TImageEnMIO to generate TIFF files in multithreading, but some of the generated images are blank without any text on them. What is the reason for this? The code is as follows:

procedure ProcessJob(const AJob: TJob);
var
  ABitmap: TIEBitmap;
  MViewIO: TImageEnMIO;
  X, Y: Integer;
  vTextW, vTextH, vOffSet: Integer;
  I, J, K: Integer;
  vText: string;
begin
  AddLog('Dealing#65306;' + IntToStr(AJob.FID));

  vOffSet := 15;
  MViewIO := TImageEnMIO.Create(nil);
  ABitmap := TIEBitmap.Create(3000, 2000, clWhite);
  ABitmap.Canvas.Lock;

  try
    try

      ABitmap.Canvas.Font.Color := clRed;
      ABitmap.Canvas.Font.Name := 'Times New Roman';
      ABitmap.Canvas.Font.Size := 50;
      vText := 'Hello Word';
      vTextW := ABitmap.Canvas.TextWidth(vText);
      ABitmap.Canvas.TextOut(Round((3000 - vTextW) / 2), 100, vText);

      ABitmap.Canvas.Font.Color := clRed;
      ABitmap.Canvas.Font.Name := 'Times New Roman';
      ABitmap.Canvas.Font.Size := 25;

      J := 0;
      K := 0;
      for I := 1 to 80 do
      begin
        X := 200 + J * 1000;
        Y := 300 + K * 50;
        ABitmap.Canvas.TextOut(X, Y, 'The ' + I.ToString + ' Testing');

        if (I mod 30) = 0 then
        begin
          Inc(J);
          K := 0;
        end
        else
          Inc(K);
      end;

      MViewIO.IEMBitmap.AppendImage(ABitmap);

      for I := 0 to MViewIO.ParamsCount - 1 do
      begin
        MViewIO.Params[I].TIFF_Compression := ioTIFF_JPEG;
        MViewIO.Params[I].TIFF_JPEGQuality := 80;
      end;
      MViewIO.SaveToFileTIFF(AJob.FSavePath + '\' + AJob.FID.ToString + '.TIFF');

    except
      on E: Exception do
      begin
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              Form1.Memo1.Lines.Add(E.Message);
            end);
        end;
      end;
    end;

    AtomicIncrement(FSuccessed);
  finally
    ABitmap.Canvas.Unlock;
    ABitmap.Clear;
    ABitmap.Free;
    MViewIO.IEMBitmap.Clear;
    MViewIO.Free;
  end;

  AddLog('ok#65306;' + IntToStr(AJob.FID));
end;

Flashcqxg

96 Posts

Posted - Jan 23 2024 :  18:37:53  Show Profile  Reply
Furthermore, multiple tests have found that it does not occur every time, but rather randomly
Go to Top of Page

xequte

38341 Posts

Posted - Jan 23 2024 :  19:14:21  Show Profile  Reply
Hi

You are using a TCanvas which converts the TIEBitmap to standard TBitmap storage.

Try redoing the code using only TIECanvas to do to the text output, i.e. using ABitmap.IECanvas.DrawText (or AdvancedDrawText):

http://www.imageen.com/help/TIECanvas.html

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 23 2024 :  19:29:12  Show Profile  Reply
when i use ABitmap.IECanvas.DrawText,the Error:
Error creating GDI+ object (#21001)
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 23 2024 :  19:36:29  Show Profile  Reply
i test with ABitmap.IECanvas.AdvancedDrawText
the same error.
Go to Top of Page

xequte

38341 Posts

Posted - Jan 23 2024 :  22:30:11  Show Profile  Reply
Please show me your code.

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 24 2024 :  08:25:02  Show Profile  Reply
I did a test, please see the attachment.


attach/Flashcqxg/202412482423_Test.zip
683.43 KB
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 24 2024 :  08:27:19  Show Profile  Reply
In the attachment, I have placed two incorrect TIFF files.
Go to Top of Page

xequte

38341 Posts

Posted - Jan 25 2024 :  20:11:50  Show Profile  Reply
Hi

Yes, that works fine for me. Even if I put the "Start Testing" button method in a 50x loop.

Can you try adding a TImageEnView to the form (as this will prevent GDI+ from being loaded and unloaded everytime a TIEBitmap is created).

What are the specs of the system and Windows version?

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

xequte

38341 Posts

Posted - Jan 25 2024 :  20:43:48  Show Profile  Reply
Hi

After some further testing I was able to reproduce a crash after about 40 iterations (40,000 images). As expected, the crash occurred due to loading/unloading of GDI+, so it was resolved by adding a TImageEnView to the form (or creating a TImageEnView at runtime and not freeing it until after processing).



Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 25 2024 :  21:00:53  Show Profile  Reply
Hello,i use windows 11.
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 25 2024 :  21:11:04  Show Profile  Reply
Now I am using TImageEnIO to generate images and write text, but I still find that some images do not have text.

procedure ProcessJob(const AJob: TJob);
var
  IO: TImageEnIO;
  FS: TFileStream;
  X, Y: Integer;
  vTextW, vTextH, vOffSet: Integer;
  I, J, K: Integer;
  vText: string;
begin

  vOffSet := 15;

  IO := TImageEnIO.Create(nil);
  FS := TFileStream.Create(AJob.FSavePath + '\' + AJob.FID.ToString + '.TIFF', fmCreate);

  IO.IEBitmap.Create(3000, 2000, clWhite);

  try
    try

      try
        IO.IEBitmap.IECanvas.Font.Color := clRed;
        IO.IEBitmap.IECanvas.Font.Name := 'Times New Roman';
        IO.IEBitmap.IECanvas.Font.Size := 50;
        vText := 'Hello ImageEn';
        vTextW := IO.IEBitmap.IECanvas.TextWidth(vText);
        IO.IEBitmap.IECanvas.DrawText(vText, Round((3000 - vTextW) / 2), 100);

        IO.IEBitmap.IECanvas.Font.Color := clRed;
        IO.IEBitmap.IECanvas.Font.Name := 'Times New Roman';
        IO.IEBitmap.IECanvas.Font.Size := 25;

        J := 0;
        K := 0;
        for I := 1 to 80 do
        begin
          X := 200 + J * 1000;
          Y := 300 + K * 50;

          IO.IEBitmap.IECanvas.DrawText('The ' + I.ToString + ' Hello Word!', X, Y);

          if (I mod 30) = 0 then
          begin
            Inc(J);
            K := 0;
          end
          else
            Inc(K);
        end;

      finally

      end;

      IO.Params.TIFF_Compression := ioTIFF_JPEG;
      IO.Params.TIFF_JPEGQuality := 80;
      IO.SaveToStreamTIFF(FS);

    except
      on E: Exception do
      begin
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              Form1.Memo1.Lines.Add('Error:' + E.Message);
            end);
        end;
      end;
    end;

    AtomicIncrement(FSuccessed);
  finally
    FS.Free;
    IO.Free;
  end;
end;
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 25 2024 :  21:49:58  Show Profile  Reply
I tested using TImageEnView, generating 1000 images at a time. During the process of generating images, if the mouse drags the form, there will still be incomplete images (no text written in the images).


procedure ProcessJob(const AJob: TJob);
var
  // IO: TImageEnIO;
  ImageEnView1: TImageEnView;
  FS: TFileStream;
  X, Y: Integer;
  vTextW, vTextH, vOffSet: Integer;
  I, J, K: Integer;
  vText: string;
begin

  vOffSet := 15;

  // IO := TImageEnIO.Create(nil);
  ImageEnView1 := TImageEnView.Create(nil);
  ImageEnView1.Visible := False;
  FS := TFileStream.Create(AJob.FSavePath + '\' + AJob.FID.ToString + '.TIFF', fmCreate);

  ImageEnView1.IEBitmap.Create(3000, 2000, clWhite);

  try
    try
      ImageEnView1.IEBitmap.Canvas.Lock;
      try
        ImageEnView1.IEBitmap.Canvas.Font.Color := clRed;
        ImageEnView1.IEBitmap.Canvas.Font.Name := 'Times New Roman';
        ImageEnView1.IEBitmap.Canvas.Font.Size := 50;
        vText := 'Hello ImageEn';
        vTextW := ImageEnView1.IEBitmap.Canvas.TextWidth(vText);
        ImageEnView1.IEBitmap.Canvas.TextOut(Round((3000 - vTextW) / 2), 100, vText);

        ImageEnView1.IEBitmap.Canvas.Font.Color := clRed;
        ImageEnView1.IEBitmap.Canvas.Font.Name := 'Times New Roman';
        ImageEnView1.IEBitmap.Canvas.Font.Size := 25;

        J := 0;
        K := 0;
        for I := 1 to 80 do
        begin
          X := 200 + J * 1000;
          Y := 300 + K * 50;

          ImageEnView1.IEBitmap.Canvas.TextOut(X, Y, 'The ' + I.ToString + ' Hello Word!');

          if (I mod 30) = 0 then
          begin
            Inc(J);
            K := 0;
          end
          else
            Inc(K);
        end;

      finally
        ImageEnView1.IEBitmap.Canvas.Unlock;
      end;

      ImageEnView1.Update;

      ImageEnView1.IO.Params.TIFF_Compression := ioTIFF_JPEG;
      ImageEnView1.IO.Params.TIFF_JPEGQuality := 80;
      ImageEnView1.IO.SaveToStreamTIFF(FS);

    except
      on E: Exception do
      begin
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              Form1.Memo1.Lines.Add('Error:' + E.Message);
            end);
        end;
      end;
    end;

    AtomicIncrement(FSuccessed);
  finally
    ImageEnView1.Blank;
    FS.Free;
    ImageEnView1.Free;
  end;
end;
Go to Top of Page

xequte

38341 Posts

Posted - Jan 26 2024 :  00:52:35  Show Profile  Reply
Hi

You should not use a visual control like TImageEnView within a thread. Please try the following demo:

http://www.imageen.com/temp/gditest.zip

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 26 2024 :  00:55:28  Show Profile  Reply
When running, I dragged the form and encountered the following error

Memo1
Error:Error creating GDI+ object (#21001)
Error:Access violation at address 6ABA287C in module 'gdiplus.dll'. Read of address 00000028
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 26 2024 :  01:32:38  Show Profile  Reply
This is my new testing program that only uses TImageEnIO.
Under multithreading, it is also unsafe as various exceptions may occur, such as errors or text that cannot be written on images.





attach/Flashcqxg/202412624956_test.zip
97.64 KB
Go to Top of Page

xequte

38341 Posts

Posted - Jan 27 2024 :  15:09:08  Show Profile  Reply
Hmm, GDI+ should be thread safe as long as the objects being used are not shared (which they are not here). We will need to investigate further.

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 28 2024 :  21:53:52  Show Profile  Reply
Thank you! I hope to find a solution soon.
Go to Top of Page

xequte

38341 Posts

Posted - Jan 30 2024 :  15:29:13  Show Profile  Reply
Detailed reading of the Microsoft documentation implies that GDI+ is not fully thread-safe.

See the Thread Synchronization at:

https://learn.microsoft.com/en-gb/windows/win32/gdiplus/sec-gdiplus

It seems that each call should be locked to avoid multiple thread access. Though that is not clear because the documentation states “when you access” the same object.

We tested modifying our code so each thread loads its own library and initializes GDI+ but it did not help. The only way would be allow only a single thread at a time to access each method of GDI+. However that would mean you you lose the advantages of multi-threading.

So, that means GDI+/TIECanvas is not your solution here. You could go back to the VCL TCanvas but it definitely has difficulty when used in threads (you should google for possible workarounds for that).

The only other possibility is that if your text was generated outside of the thread and put into a TIEBitmap, then that TIEBitmap could be drawn onto another TIEBitmap in multi-threaded code.


Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Flashcqxg

96 Posts

Posted - Jan 30 2024 :  19:11:29  Show Profile  Reply
thanks.
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: