Загрузка ресурсов в потоке.
От: PavZ  
Дата: 23.07.03 16:55
Оценка:
Использую для отображения хода загрузки второй поток, в котором написано примерно следующее...

/// ThreadMainProcedure ///

repeat
state = not state
CopyToPrimarySurface(At_x,At_y, BackBuffer[state]);
until false;

///////////////////////////

т.е. происходит смена изображения на экране по аналогии с Flip , только Flip тут не подходит из-за
оконного режима. Типа получается в результате мерцание надписи Loading...


Проблемы начинаются при перетаскивании окна во время загрузки...т.к. нужно менять At_x,At_y и делать
их = СlientRect.x, y

пробовал делать так:

TMyThread.SetScreen;
begin
At_x = MyForm.ClientRect.x
At_x = MyForm.ClientRect.y
end;

и в цикле перед копированием вставлять

Syncronize(SetScreen);

но при этом поток виснет... на экран ничего не копируется.
Самое интересное, что даже если процедуру SetScreen сделать пустой,
то все равно виснет на вызове Syncronize


Основная проблема также в том что когда грузяться ресурсы не обрабатываются сообщения в программе.
Сделать именно загрузку ресурсов в другом потоке — получается не очень успешно, т.к. некоторые вещи
начинают очень странно работать, похоже проблемы с синхронизацией.
Внутри загрузки интенсивно используются объекты TJpegImage, TBitmap, IDirectDrawSurface. В некоторых
случаях результирующая картинка получается порченой, cлетают шрифты при использовании объекта TFont.
При работе в основном потоке все загружается нормально.

Cоотв. самый простой способ — оставить загрузку в основном потоке а индикатор в доп. процесс,
но тут начинаются траблы с тем что окно не обрабатывает сообщения , вставил Application.ProcessMessages,
начали сыпаться эксепшены...acc. viol., и нет возможности менять значения переменных At_x,At_y. Которые
должны соотв. коорд левого верхего угла формы.


Но хотелось бы сделать загрузку в доп. потоке а индикатор менять уже в цикле сообщений основного потока,
тогда остается один вопрос — как пофиксить порчу информации при работе с выше перечисленными объектами ...

Наверняка кто-то уже это все делал, так что пишите буду очень благодарен за разъяснения, т.к. чувствую
что в каком-то месте туплю .
Re: Загрузка ресурсов в потоке.
От: PavZ  
Дата: 24.07.03 10:54
Оценка:
Вот сделал пример кода который не работает. Т.е. изображение в памяти не будет соотв. изображению в файле
test.bmp. (test.bmp класть в тот же каталог где exe файл ).
Картинку лучше сделать чернобелой...чтобы легче было сравнивать.

Но если сделать загрузку ( вызвать LoadRes ) из основного потока, то все будет ок.

TMyThread = class(TThread)
  procedure Execute; override;
end;

var
  Form1: TForm1;

 load_thread:TMyThread;
  // Указатель на память в которую будут загружаться ресурсы.
  mem:pointer = nil;
  // Состояние загрузки , 1-идет загрузка, >2 - загрузка окончена
  loading_state:byte=0;
  // Ширина и Высота картинки изпользуемой при загрузке
  w,h:integer;
implementation

{$R *.DFM}

procedure LoadRes;
var
 bmp:TBitmap;
 i,j:integer;
begin
 bmp:=TBitmap.Create; bmp.LoadFromFile('test.bmp');
 // запоминаем ширину и высоту
 w:=bmp.Width; h:=bmp.Height;
 // выделяем один раз память.
 if mem = nil then mem:=allocmem(bmp.Width*bmp.Height);
 // заполняем память
 for i:=0 to bmp.Width-1 do for j:=0 to bmp.Height-1 do
  if bmp.Canvas.Pixels[i,j]<>clBlack then PByte(Integer(mem)+j*bmp.Width+i)^:=1
  else PByte(Integer(mem)+j*bmp.Width+i)^:=0;

 bmp.Free;
end;


procedure TMyThread.Execute;
var
 i:integer;
begin
 loading_state:=1;  // помечаем начало загрузки
   for i:=0 to 20 do LoadRes;  // несколько раз чтобы помедленее было.
 loading_state:=2;  // конец загрузки
end;


procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
  var Done: Boolean);
begin
 Done:=false;

 Label1.Visible:=(loading_state=1); // Делаем метку 'Loading...' видимой если идет загрузка


 Tag:=1-Tag;
 Label1.Color:=RGB(Tag*255,0,0); // меняем по очереди цвет лэйбла с красного на черный...

 if Loading_State=2 then FormPaint(Sender); // как только загрузка окончена, выводим результат на форму
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 // Запускаем поток для загрузки
 loading_state:=0;
 load_thread:=TMyThread.Create(false);
end;

procedure TForm1.FormPaint(Sender: TObject);
var
 i,j:integer;
begin
 if loading_state>=2 then begin

{  Разкомментировать для проверки что в основном потоке все грузиться ок. }
//    if loading_state=2 then LoadRes;

  loading_state:=3; // чтобы не грузить каждый раз при перерисовке окна, а также
                    // запретить вызовы OnPaint из цикла в OnIdle;

// Делаем размер формы = размеру картинки
   Width:=w; Height:=h;
// Выводим на форму содержимое памяти.
   for i:=0 to w-1 do for j:=0 to h-1 do
    if PByte(Integer(mem)+i+j*w)^=0 then Canvas.Pixels[i,j]:=clBlack
     else Canvas.Pixels[i,j]:=clWhite;
 end;
end;
Re: Исходник
От: PavZ  
Дата: 24.07.03 14:03
Оценка:
Исходник:
http://jdm.narod.ru/TestThread.zip

(Delphi 5)
Re[2]: Исходник
От: troits  
Дата: 24.07.03 15:00
Оценка: 9 (1)
Здравствуйте, PavZ, Вы писали:

PZ>Исходник:

PZ>http://jdm.narod.ru/TestThread.zip

PZ>(Delphi 5)



При работе с Canvas из thread-а надо его "лочить"

bmp.Canvas.Lock;
try
for i:=0 to bmp.Width-1 do
for j:=0 to bmp.Height-1 do
begin
if bmp.Canvas.Pixels[i,j]<>clBlack then
PByte(Integer(mem)+j*bmp.Width+i)^:=1
else
PByte(Integer(mem)+j*bmp.Width+i)^:=0;
end;
finally
bmp.Canvas.Unlock;
end;

работает медленнее, но тут уж надо оптимизировать...
Re[3]: Исходник
От: PavZ  
Дата: 24.07.03 17:10
Оценка:
T>При работе с Canvas из thread-а надо его "лочить"

T> bmp.Canvas.Lock;

T> try
T> for i:=0 to bmp.Width-1 do
T> for j:=0 to bmp.Height-1 do
T> begin
T> if bmp.Canvas.Pixels[i,j]<>clBlack then
T> PByte(Integer(mem)+j*bmp.Width+i)^:=1
T> else
T> PByte(Integer(mem)+j*bmp.Width+i)^:=0;
T> end;
T> finally
T> bmp.Canvas.Unlock;
T> end;

T> работает медленнее, но тут уж надо оптимизировать...


Спасибо ! Работает !
Re[2]: Исходник
От: Denom Украина  
Дата: 25.07.03 12:44
Оценка:
Здравствуйте, PavZ, Вы писали:

PZ>Исходник:

PZ>http://jdm.narod.ru/TestThread.zip

PZ>(Delphi 5)

Попробуйте так:
  // Указатель на память в которую будут загружаться ресурсы.
  mem: TBitmap; //в буффер памяти копироать не нужно. т.к рисовать на форме...
  // Состояние загрузки , 1-идет загрузка, >2 - загрузка окончена
  loading_state: byte = 0;
  // Ширина и Высота картинки изпользуемой при загрузке
  w, h: integer;

implementation

{$R *.DFM}

procedure LoadRes;
var
  bmp: TBitmap;
  i, j: integer;
begin
  bmp := tbitmap.Create;
  bmp.LoadFromFile('test.bmp');
  // запоминаем ширину и высоту
  w := bmp.Width;
  h := bmp.Height;
  // выделяем один раз память.
    //освободим в FormDestroy 
  if mem = nil then
  begin
    mem := TBitmap.Create;
    mem.Height := bmp.Height;
    mem.Width := bmp.Width;
    mem.PixelFormat := bmp.PixelFormat;
  end;
  // заполняем память
  bmp.Canvas.Lock;
  mem.Canvas.Lock;
  try
    mem.Canvas.Draw(0,0,bmp);//это вместо копировния в массив байт.
  finally
    mem.Canvas.UnLock;
    bmp.Canvas.UnLock;
  end;
  bmp.Free;
end;


procedure TMyThread.Execute;
var
  i: integer;
begin
  loading_state := 1;  // помечаем начало загрузки
  for i := 0 to 20 do LoadRes;  // несколько раз чтобы помедленее было.
  loading_state := 2;  // конец загрузки
end;


procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
  var Done: boolean);
begin
  Done := False;

  Label1.Visible := (loading_state = 1);
  // Делаем метку 'Loading...' видимой если идет загрузка
  // По идее можно рисовать прямо на канве формы...

  Tag := 1 - Tag;
  Label1.Color := RGB(Tag * 255,0,0);
  // меняем по очереди цвет лэйбла с красного на черный...

  if Loading_State = 2 then 
    begin
      // Делаем размер формы = размеру картинки
    Width := w;
    Height := h;
      FormPaint(Sender);
    end;
  // как только загрузка окончена, выводим результат на форму
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Запускаем поток для загрузки
  loading_state := 0;
  load_thread := TMyThread.Create(False);
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  i, j: integer;
begin
  if loading_state >= 2 then
  begin
    {  Разкомментировать для проверки что в основном потоке все грузиться ок. }
    //    if loading_state=2 then LoadRes;

    loading_state := 3; // чтобы не грузить каждый раз при перерисовке окна, а также
    // запретить вызовы OnPaint из цикла в OnIdle;

    // Выводим на форму содержимое памяти.
    Canvas.Draw(0,0,mem);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(mem);
end;
... << RSDN@Home 1.1 beta 1 >>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.