Перейти к содержанию
Fire Monkey от А до Я
  • 0

TBitmapData


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.

 

Ссылка на комментарий

Рекомендуемые сообщения

  • 1

Считалось, что до RIO TBitmap НЕ потокобезопасен. В RIO рапортовали, что справились с этим. Не помню, проверял кто или нет

Но TBitmapSurface был потокобезопасен уже тогда. Возможно, стоит готовить данные там, а потом просто отдавать в Bitmap

https://stackoverflow.com/questions/37602538/delphi-tbitmap-to-string-via-tbitmapsurface-and-back-to-tbitmap

https://stackoverflow.com/questions/51523321/how-to-draw-fmx-surface-tbitmapsurface-on-fmx-graphics-tbitmap/51526855

 

Ссылка на комментарий
  • 0

Попробовал TBitmapSurface. Работает стабильно. Спасибо! Ниже аналогичная реализация с TBitmapSurface. Но, увы, эта реализация работает медленнее. Изучив исходники  TBitmapSurface, стало понятно почему: а) в TBitmapSurface.SetSize происходит прописывание нулями всего массива данных(спорное решение) б) в TBitmap.Assign(TPersistent), если параметр TBitmap, то происходит лишь копирование ссылки, а если TBitmapSurface, то копирование всего битмапа. В своей реализации пошел другим путем, но изучение исходников TBitmapSurface в этом сильно помогло. Ещё раз спасибо.

По поводу «потокобезопастности» TBitmap в RIO, однозначно нет в Win32-64, и мой пример тому доказательство, но судя по коду, есть ощущение, что с ARC в Android и прочих, все будет работать «безопасно», но это только догадка, без фактической проверки.

function GetFMXBitmapSurface: TBitmapSurface ;
var
    Count_X,Count_Y:NativeInt;
    CurrentColor:TAlphaColor;
begin
 Result := TBitmapSurface.Create;
 Result.SetSize(640,512);
 for Count_X := 0 to Result.width-1 do
  for Count_Y := 0 to Result.height-1 do
   begin
    TAlphaColorRec(CurrentColor).R:=Random(255);
    TAlphaColorRec(CurrentColor).G:=Random(255);
    TAlphaColorRec(CurrentColor).B:=Random(255);
    TAlphaColorRec(CurrentColor).A:=$FF;
    Result.Pixels[Count_X,Count_Y]:=CurrentColor;
   end;
end;

 

Изменено пользователем ddr 2
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...