Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Звук, графика и видео > Создание миниатюр в ListView


Автор: TechnoMag 14.10.2013, 17:16
При выборе папки с изображениями могут создаться все миниатюры, для все файлов, а иногда бывает, что файлы пропускаются.
Не пойму: проблема в реализации потока, или в компоненте? Пробовал на Win7 x64 и WinXp x32.
Просьба указать проблемные места в коде.
Прикрепляю файл проекта XE2.

Автор: Illusion Dolphin 14.10.2013, 17:32
Код

FThumbsListView.LargeImages.Clear;

Не рекомендуется в потоке вызывать

Код

        FTempBMP.Canvas.Brush.Color := clWhite;
        FTempBMP.Canvas.FillRect(RECT(0, 0, 128, 128));

Код

// растягиваем
            StretchBlt(FNewImage.canvas.handle,

Не рекомендуется пользоваться канвой в потоке


Автор: TechnoMag 14.10.2013, 18:25
А как быть, если нужно, чтобы окно приложения не подвисало во время создания большого кол-ва миниатюр? С ProcessMessages в основном потоке все равно программа немного подвисает.
З.Ы. FTempBMP и FNewImage не привязаны к окну, и получается, что рисование происходит в памяти, а потом, синхронизированно, добавляются в ListView.

Автор: Illusion Dolphin 14.10.2013, 18:30
Цитата

А как быть, если нужно, чтобы окно приложения не подвисало во время создания большого кол-ва миниатюр?

OwnerData = true это раз. Ещё синхронизация - это долгий и нудный процесс, поэтому данные из потока лучше выдавать пачками, и в каждой пачке делать 
Код

  FThumbsListView.Items.BeginUpdate;
  try
    ... //добавляем пачку данных тута
  finally
    FThumbsListView.Items.EndUpdate;
  end;


Цитата

FTempBMP и FNewImage не привязаны к окну, и получается, что рисование происходит в памяти

И что? Canvas не рассчитано на многопоточность.

Автор: TechnoMag 23.10.2013, 19:55
Эффект тот же: иногда "рисуются" "пустые" миниатюры в ListView.
Про выдачу данных пачками пока опускаем - нужно сначала понять, что мешает добавляться всем миниатюрам в список.

Код

procedure TImageLoader.AddToListView1;
var
  listItem: TListItem;
begin
  SetStretchBltMode(FNewImage.canvas.handle, 4);// мягкое растягивание
  StretchBlt(FNewImage.canvas.handle,
                          (FNewImage.Width div 2) - (NewWidth div 2), // располагаем по центру по горизониали
                           (FNewImage.Height div 2) - (NewHeight div 2), // располагаем по центру по вертикали
                           NewWidth, NewHeight,
                           Image.canvas.handle,
                           0,
                           0,
                           Image.width,
                           Image.height,
                           SRCCOPY);
  FMask.Assign(FNewImage);
  listItem := FThumbsListView.Items.Add;
  listItem.ImageIndex := FThumbsListView.LargeImages.Add(FNewImage, FMask); // добавляем миниатюру в список
  listItem.Caption := ExtractFileName(FFileName);
end;

procedure TImageLoader.AddThumb1(const FilePath: string);
var
  jpg: TJpegImage;
begin
   Image := TBitmap.Create;
   try
     jpg := TJpegImage.Create;
     try
        if ExtractFileExt(LowerCase(FilePath))='.jpg' then
        begin
            jpg.LoadFromFile(FilePath); // загружаем изображение из файла
            Image.Assign(jpg);
        end;
        if ExtractFileExt(LowerCase(FilePath))='.bmp' then
            Image.LoadFromFile(FilePath);
        // создаем миниатюру
        FNewImage := TBitmap.Create;
        try
          FNewImage.width := 128;
          FNewImage.Height := 128;
          FNewImage.pixelFormat:=pf32bit;
          //SetStretchBltMode(FNewImage.canvas.handle, 4);// мягкое растягивание
          // изменяем размер
          SetSize(Image.Width, Image.Height, NewWidth, NewHeight);
          FMask := TBitmap.Create;
          try
            FMask.Width := NewWidth;
            FMask.Height := NewHeight;
            FMask.Mask(clWhite);
            // растягиваем
            Synchronize(AddToListView1);
          finally
            FMask.Free;
          end;
        finally
          FNewImage.Free;
        end;
     finally
       Jpg.Free;
     end;
   finally
     Image.Free;
   end;
end;

Автор: Illusion Dolphin 24.10.2013, 00:15
Сложно сказать наверняка, давайте я прокоментирую пару строчек, а вы потом ещё опишите, как выглядят пустые изображения (всегда пустое или если перегрузить директорию - грузится? какой цвет у изображения? не может ли быть такое что 2 потока одновременно грузят изображение в 1 лист?)
Цитата

   Image := TBitmap.Create;
   try
     jpg := TJpegImage.Create;
     try
        if ExtractFileExt(LowerCase(FilePath))='.jpg' then
        begin
            jpg.LoadFromFile(FilePath); // загружаем изображение из файла
            Image.Assign(jpg); //тут происходит лёгкая блокировка UI из-за реализации  Bitmap.Assign с критическими секциями
        end;
        if ExtractFileExt(LowerCase(FilePath))='.bmp' then
            Image.LoadFromFile(FilePath);
        //а если расширение .Bmp или .jpEg ? упс - чистая картинка будет

        // создаем миниатюру
        FNewImage := TBitmap.Create;
        try
          FNewImage.width := 128;//надо SetSize - быстрее
          FNewImage.Height := 128;
          FNewImage.pixelFormat:=pf32bit; //перед изменением размера указывайте, быстрее
          //SetStretchBltMode(FNewImage.canvas.handle, 4);// мягкое растягивание
          // изменяем размер
          SetSize(Image.Width, Image.Height, NewWidth, NewHeight);
          FMask := TBitmap.Create;
          try
            FMask.Width := NewWidth;
            FMask.Height := NewHeight;//надо SetSize - быстрее
            FMask.Mask(clWhite);//внутри странная реализация, не нравится мне оно в потоке, пробывали выносить?
            // растягиваем
            Synchronize(AddToListView1);
          finally
            FMask.Free;
          end;
        finally
          FNewImage.Free;
        end;
     finally
       Jpg.Free;
     end;
   finally
     Image.Free;
   end;
end;

Да и способ на 100% - постепенно выносить из потока в Synchronize фугкция за функцию и вы найдёте то, что ищете smile 

Автор: TechnoMag 24.10.2013, 12:03
Цитата

Сложно сказать наверняка, давайте я прокоментирую пару строчек, а вы потом ещё опишите, как выглядят пустые изображения (всегда пустое или если перегрузить директорию - грузится? какой цвет у изображения? не может ли быть такое что 2 потока одновременно грузят изображение в 1 лист?)


Пустое изображение содержит только название файла, область, где должна быть миниатюра таким же цветом как и фон ListView.
Грузит изображения только один поток. Если перезагрузить директорию изображения могут загрузиться.

Цитата

Да и способ на 100% - постепенно выносить из потока в Synchronize фугкция за функцию и вы найдёте то, что ищете


Ну да, сыпать иронией, этот так по умному - сразу будет всем видно, что человек что-то знает и понимает, а главное - знает какими способами еще можно найти причину неполадки.


Автор: MetalFan 24.10.2013, 12:40
странная логика - вынос в synchronize самой ресурсоемкой операции...
я бы заюзал GDI+, полностью в отдельном треде. с передачей данных в/из него в memory stream или tbytes.

Автор: Illusion Dolphin 24.10.2013, 17:15
Цитата

странная логика - вынос в synchronize самой ресурсоемкой операции...

Как я понимаю - это временно до выяснения причины "пробелов" в загрузке
Цитата

Ну да, сыпать иронией, этот так по умному - сразу будет всем видно, что человек что-то знает и понимает, а главное - знает какими способами еще можно найти причину неполадки.

Вы зря иронизируете. Проблема в реализации внутри VCL (в котором также есть баги), которая может меняться от версии к версии как минимум и в http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Graphics_TBitmap.html нет чётких данных про thread-safe для каждого метода. Вам, наверное, ещё не приходилось удалять из большого проекта 99% кода, чтобы найти сбойный участок, который может быть ну очень неочевиден. 

P.S. У меня в подписи ссылка на сайт с программой - попробуйте установите и откройте в ней вашу папку с фотографиями. Если вам понравится, то я могу вам помочь.

Автор: WERITAS 21.5.2017, 11:03
Поднимаю тему!
Столкнулся с такой же проблемой: миниатюра в потоке криво формируется. Билдил код автора и смотрел как выполняется - у меня такой же эффект. Как это победить?

Решение найдено!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)