Требуется на форме отображать картинку TImage(кадр) обновляя её с высокой частотой. Обновляемая картинка динамически формируется попиксельно. Попиксельный доступ реализован через TBitmapData. Формирование кадра достаточно ресурсоёмко. Есть желание вынести формирование кадра в отдельный поток. Но опытным путем я выяснил, что TBitmapData нельзя использовать вне основного потока. Ниже тестовая демонстрационная реализация. Формирование кадра вынесено в функцию GetFMXBitmap. Если bmp := GetFMXBitmap; вызывается через Synchronize, то код на 100% потокобезопасен, если же вызов идет без Synchronize, то приложение валиться, достаточно лишь мышкой энергично изменять размеры окна(формы). Вопрос: TBitmapData полученный через TBitMap.Mapдействительно можно использовать только из основного потока, илия допустил какую-то ошибку? Если только основной поток, то как бы поизящней обойти это ограничение?
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects;
type
TTestThread=class(TThread)
Private
Protected
Procedure execute;override;
Public
constructor create();
end;
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
TestTthread: TTestThread;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
function GetFMXBitmap: FMX.Graphics.TBitmap;
var bitdata: TBitmapData;
Count:Cardinal;
begin
Result := FMX.Graphics.TBitmap.Create;
Result.SetSize(640,512);
if (Result.Map(TMapAccess.ReadWrite{Write}, bitdata)) then
try
// здесь "рисуем" через bitdata
for count := 0 to Result.width*Result.height-1 do
begin
pbyte(bitdata.Data)[4*count]:=Random(255);
pbyte(bitdata.Data)[4*count+1]:=Random(255);
pbyte(bitdata.Data)[4*count+2]:=Random(255);
pbyte(bitdata.Data)[4*count+3]:=$FF;
end;
finally
Result.Unmap(bitdata);
end;
end;
constructor TTestThread.create();
begin
FreeOnTerminate := true;
inherited Create(false);
end;
Procedure TTestThread.execute;
var bmp: FMX.Graphics.TBitmap;
begin
while not Terminated do
begin
{Вариант 1- следующая строчка закоментарена, Вариант 2- следующая строчка НЕ закоментарена }
//bmp := GetFMXBitmap;
TThread.Synchronize(nil, procedure
begin
{Вариант 1- следующая строчка НЕ закоментарена, Вариант 2- следующая строчка закоментарена }
bmp := GetFMXBitmap;
Form1.Image1.Bitmap.Assign(bmp);
bmp.Free;
end);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Image1.Width:=640;
Image1.Height:=512;
TestTthread:=TTestThread.create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
TestTthread.Terminate;
end;
end.
Вопрос
ddr 2
Требуется на форме отображать картинку TImage(кадр) обновляя её с высокой частотой. Обновляемая картинка динамически формируется попиксельно. Попиксельный доступ реализован через TBitmapData. Формирование кадра достаточно ресурсоёмко. Есть желание вынести формирование кадра в отдельный поток. Но опытным путем я выяснил, что TBitmapData нельзя использовать вне основного потока. Ниже тестовая демонстрационная реализация. Формирование кадра вынесено в функцию GetFMXBitmap. Если bmp := GetFMXBitmap; вызывается через Synchronize, то код на 100% потокобезопасен, если же вызов идет без Synchronize, то приложение валиться, достаточно лишь мышкой энергично изменять размеры окна(формы). Вопрос: TBitmapData полученный через TBitMap.Map действительно можно использовать только из основного потока, или я допустил какую-то ошибку? Если только основной поток, то как бы поизящней обойти это ограничение?
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects; type TTestThread=class(TThread) Private Protected Procedure execute;override; Public constructor create(); end; TForm1 = class(TForm) Image1: TImage; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } TestTthread: TTestThread; end; var Form1: TForm1; implementation {$R *.fmx} function GetFMXBitmap: FMX.Graphics.TBitmap; var bitdata: TBitmapData; Count:Cardinal; begin Result := FMX.Graphics.TBitmap.Create; Result.SetSize(640,512); if (Result.Map(TMapAccess.ReadWrite{Write}, bitdata)) then try // здесь "рисуем" через bitdata for count := 0 to Result.width*Result.height-1 do begin pbyte(bitdata.Data)[4*count]:=Random(255); pbyte(bitdata.Data)[4*count+1]:=Random(255); pbyte(bitdata.Data)[4*count+2]:=Random(255); pbyte(bitdata.Data)[4*count+3]:=$FF; end; finally Result.Unmap(bitdata); end; end; constructor TTestThread.create(); begin FreeOnTerminate := true; inherited Create(false); end; Procedure TTestThread.execute; var bmp: FMX.Graphics.TBitmap; begin while not Terminated do begin {Вариант 1- следующая строчка закоментарена, Вариант 2- следующая строчка НЕ закоментарена } //bmp := GetFMXBitmap; TThread.Synchronize(nil, procedure begin {Вариант 1- следующая строчка НЕ закоментарена, Вариант 2- следующая строчка закоментарена } bmp := GetFMXBitmap; Form1.Image1.Bitmap.Assign(bmp); bmp.Free; end); end; end; procedure TForm1.FormCreate(Sender: TObject); begin Image1.Width:=640; Image1.Height:=512; TestTthread:=TTestThread.create; end; procedure TForm1.FormDestroy(Sender: TObject); begin TestTthread.Terminate; end; end.
Ссылка на комментарий
2 ответа на этот вопрос
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.