Author |
Topic |
|
Flashcqxg
96 Posts |
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;
|
|
Flashcqxg
96 Posts |
Posted - Jan 23 2024 : 18:37:53
|
Furthermore, multiple tests have found that it does not occur every time, but rather randomly |
|
|
xequte
38686 Posts |
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
96 Posts |
Posted - Jan 23 2024 : 19:29:12
|
when i use ABitmap.IECanvas.DrawText,the Error: Error creating GDI+ object (#21001) |
|
|
Flashcqxg
96 Posts |
Posted - Jan 23 2024 : 19:36:29
|
i test with ABitmap.IECanvas.AdvancedDrawText the same error. |
|
|
xequte
38686 Posts |
Posted - Jan 23 2024 : 22:30:11
|
Please show me your code.
Nigel Xequte Software www.imageen.com
|
|
|
Flashcqxg
96 Posts |
|
Flashcqxg
96 Posts |
Posted - Jan 24 2024 : 08:27:19
|
In the attachment, I have placed two incorrect TIFF files. |
|
|
xequte
38686 Posts |
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
|
|
|
xequte
38686 Posts |
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
|
|
|
Flashcqxg
96 Posts |
Posted - Jan 25 2024 : 21:00:53
|
Hello,i use windows 11. |
|
|
Flashcqxg
96 Posts |
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
96 Posts |
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; |
|
|
xequte
38686 Posts |
|
Flashcqxg
96 Posts |
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
|
|
|
Flashcqxg
96 Posts |
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 |
|
|
xequte
38686 Posts |
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
96 Posts |
Posted - Jan 28 2024 : 21:53:52
|
Thank you! I hope to find a solution soon. |
|
|
xequte
38686 Posts |
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
96 Posts |
Posted - Jan 30 2024 : 19:11:29
|
thanks. |
|
|
|
Topic |
|