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

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 TImageEnMIO cannot generate images correctly in multithreading

Note: You must be registered in order to post a reply.
To register, click here. Registration is FREE!

View 
UserName:
Password:
Format  Bold Italicized Underline  Align Left Centered Align Right  Horizontal Rule  Insert Hyperlink   Browse for an image to attach to your post Browse for a zip to attach to your post Insert Code  Insert Quote Insert List
   
Message 

 

Emoji
Smile [:)] Big Smile [:D] Cool [8D] Blush [:I]
Tongue [:P] Evil [):] Wink [;)] Black Eye [B)]
Frown [:(] Shocked [:0] Angry [:(!] Sleepy [|)]
Kisses [:X] Approve [^] Disapprove [V] Question [?]

 
Check here to subscribe to this topic.
   

T O P I C    R E V I E W
Flashcqxg Posted - Jan 23 2024 : 18:23:47
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;
19   L A T E S T    R E P L I E S    (Newest First)
Flashcqxg Posted - Jan 30 2024 : 19:11:29
thanks.
xequte Posted - Jan 30 2024 : 15:29:13
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
Flashcqxg Posted - Jan 28 2024 : 21:53:52
Thank you! I hope to find a solution soon.
xequte Posted - Jan 27 2024 : 15:09:08
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
Flashcqxg Posted - Jan 26 2024 : 01:32:38
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
Flashcqxg Posted - Jan 26 2024 : 00:55:28
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
xequte Posted - Jan 26 2024 : 00:52:35
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
Flashcqxg Posted - Jan 25 2024 : 21:49:58
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;
Flashcqxg Posted - Jan 25 2024 : 21:11:04
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;
Flashcqxg Posted - Jan 25 2024 : 21:00:53
Hello,i use windows 11.
xequte Posted - Jan 25 2024 : 20:43:48
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
xequte Posted - Jan 25 2024 : 20:11:50
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
Flashcqxg Posted - Jan 24 2024 : 08:27:19
In the attachment, I have placed two incorrect TIFF files.
Flashcqxg Posted - Jan 24 2024 : 08:25:02
I did a test, please see the attachment.


attach/Flashcqxg/202412482423_Test.zip
683.43 KB
xequte Posted - Jan 23 2024 : 22:30:11
Please show me your code.

Nigel
Xequte Software
www.imageen.com
Flashcqxg Posted - Jan 23 2024 : 19:36:29
i test with ABitmap.IECanvas.AdvancedDrawText
the same error.
Flashcqxg Posted - Jan 23 2024 : 19:29:12
when i use ABitmap.IECanvas.DrawText,the Error:
Error creating GDI+ object (#21001)
xequte Posted - Jan 23 2024 : 19:14:21
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
Flashcqxg Posted - Jan 23 2024 : 18:37:53
Furthermore, multiple tests have found that it does not occur every time, but rather randomly