| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Непонятно ведет себя TPicture в потоке |
| Автор: Ne1tr1n0 31.8.2012, 15:46 | ||
| Добрый день! Пытаюсь сделать просмотр миниатюр в ListView в виртуальном режиме. Делаю так: сначала получаю список файлов в каталоге, заношу в TStringList с помощью AddObject(sr.Name, TObject(IconIndex)) где IconIndex - индекс иконки файла в моем ImageList'e, полученный с помощью SHGetFileInfo. Далее запускается поток, в котором происходит следующее:
Причем эта ошибка возникает нерегулярно, на одной и той же картинке может нормально отработать, а может и выдать черный или белый квадрат. Если надо - могу полностью скинуть проект. Заранее спасибо. |
| Автор: Dapo 31.8.2012, 17:38 |
| Кто такие BmpCanvas, FFileList, FImageList? F каком потоке они создаются? Почему Вы пилите сук на котором сидите: FBmp.Free; //убираем за собой |
| Автор: Illusion Dolphin 31.8.2012, 20:33 | ||||
Ошибки тут:
и тут:
С объектом Canvas работать из потоков нельзя, только через синхронизацию. |
| Автор: Ne1tr1n0 2.9.2012, 01:41 | ||
| Вынес работу с Canvas в процедуру синхронизации. Но появились тормоза при прокрутке ListView например. Да и вообще интерфейс стал менее отзывчивым на действия пользователя. Вот код:
Как теперь можно сделать? Добавлено через 3 минуты и 19 секунд ЗЫ: Я так полагаю основные тормоза создает StretchDraw. Возможно с ним что-нить можно сделать? |
| Автор: Illusion Dolphin 2.9.2012, 12:01 |
| Каждый из этих методов создаёт тормоза. Assign одного TGraphic к другому везде реализован по-разному, в зависимостиот этого это можно выполнять в потоке или надо писать ручной Assign, который не будет затрагивать Canvas. Это зависит от конкретных типов изображений. StretchDraw - тут уже полегче (если не заморачиваться с качеством), есть готовые решения типа http://forum.vingrad.ru/forum/topic-49118/hl/resize/index.html, они не работают с канвой, поэтому это можно делать в потоке без синхронизации. |
| Автор: Ne1tr1n0 2.9.2012, 13:58 | ||
| Поискал ещё насчет канвы и потоков - действительно пишут, что да, класс не Thread-safe, так что могут быть проблемы. Некоторые рекомендуют использовать методы Canvas.Lock и Canvas.Unlock соответственно сразу после того как создали битмап и закончили с ним работать. Вроде как это специальные методы Borland/Embarcadero (не знаю точно когда они появились), позволяющие работать с канвой в многопоточных приложениях. Попробовал у себя использовать - не помогает, хотя ошибок стало заметно меньше. Может быть стоит покопать поглубже в сторону этих Lock/Unlock? Есть смысл? Вот код с блокированием канвы:
Насчет ресайза без использования канвы - спасибо, тоже посмотрю сейчас |
| Автор: MetalFan 2.9.2012, 14:26 |
| Я тоже сталкивался с проблемами при работе с TBitmap/TPicture в потоке... На сколько я помню, TBitmap'у не удавалось порой выделять память при использовании его в отдельном потоке. При чем при работе в осн.потоке проблем не возникало. Как решил проблему уже не помню, возможно свел вероятность ее возникновение к минимуму. |
| Автор: Illusion Dolphin 2.9.2012, 20:32 | ||||
Этот код блокирует все другие операции TBitmap.Assign ра время работы во всех потоках (инфа: исходники VCL)
Возможно это решит часть проблем, но я это обходил через неиспользование TCanvas в потоках. |
| Автор: Ne1tr1n0 3.9.2012, 11:58 | ||
Что-то все равно проскакивают ошибки. Может я что не так делаю?
|
| Автор: MetalFan 3.9.2012, 13:42 |
| Ne1tr1n0, А FImgList - эт случайно не ссылка на компонент на форме? может лучше это (добавление картинки в ImageList) делать в основном потоке через Synchronize? |
| Автор: Ne1tr1n0 3.9.2012, 13:45 |
| Так точно, она и есть. Это ImageList, привязанный к ListView'у. Сейчас попробую вынессти добавление в ImageList в Syncronize. |
| Автор: Ne1tr1n0 3.9.2012, 15:59 |
| Да, так работает. И вроде даже не тормозит. Я тут просто решил поизвращаться, создал класс TItemData, в нем одним из полей TMemoryStream, так вот, после SmoothResize я запихивал тумбу в TJPEGImage, сжимал её, и сохранял в этот TMemoryStream. И добавлял каждый экземпляр класса в качестве связанного объекта в список файлов. А в ListView.OnGetImageIndex уже доставал из потока, преобразовывал в битмап и добавлял его в ImageList, назначая индекс только что добавленного элемента каждому ListItem'у. Тоже работало, но вариант MetalFan'a мне как-то поизящней кажется. Пожалуй тему можно закрывать, всем спасибо за обсуждение |
| Автор: MetalFan 3.9.2012, 20:47 | ||
Ватэто изврат) Типа память экономил? А проблема скорее всего была в вызове метода TImageList.Add из потока... что вызывало скорее всего Update и выполнение кучи VCL-ного кода в доп.потоке вместо основного... Не зря ж везде советуют НЕ использовать VCL компоненты в доп. потоках без синхронизации или без понимания внутреннего устройства. |
| Автор: Ne1tr1n0 3.9.2012, 22:17 | ||
| Ну можно и так сказать))) Только все равно тумба потом в ImageList'e оказывалась в виде битмапа, так что толку от такой экономии не особо было. Там просто одна из мыслей была ещё в OnCustomDrawItem самому её отрисовывать, но отказался от этой затеи. И в каком-то примере видел подобную штуку, но там свой контрол был, внешне напоминающий ListView, но с собственным механизмом отрисовки, там как раз из жпега всё рисовалось. Тут ещё попутно вопрос возник. Не совсем правда к этой теме относящийся, так что может лучше и в отдельную ветку вынести. Вот делаю я значит в процедуре синхронизации такую штуку:
А после приведенного вызова Canvas.Draw видимо из-за того, что для FBmp в SmoothResize явно задается PixelFormat:=pf24bit у меня прозрачность теряется. И никакими TransparentColor/TransparentMode или PixelFormat:=pf32bit вернуть её не удается. В результате в ListView, если выделить элемент, то вокруг изображения образуется белый фон (вместо синего выделения). Выглядит это примерно так (у выделенного итема сверху и снизу относительно широкие белые полосы): Вот как бы от этого ещё избавиться? Спасибо. |
| Автор: Ne1tr1n0 4.9.2012, 18:05 |
| Вроде разобрался. Создал маску, теперь с ней добавляю в ImageList |
| Автор: Nialon 5.10.2012, 21:31 | ||
Прочитал шапку, уже все понял. Перед финальным кодом надо обязательно синхронизировать !весь! потоковый блок. Особенно если не разбираетесь или не читали литературу где написано что любое обращение к VCL надо синхронизировать. Как только все идет по маслу, начинаете укорачивать блоки синхронизации до не безопасных строк. и все. У меня точно такой же код с загрузкой, программа периодически сыпалась, что есть самый 1 признак, на LoadFromFile и все в таком духе. К сожалению, с ListView работал в другом режиме, догадываюсь зачем DrawIcon, там вроде список из иконок, но без понятия куда, точнее для чего, вы отрисоваете BmpCanvas, как и что .... DrawIcon специально создана для иконок и она учитывает их прозрачность, рисуя на холсте. Даже DrawIcon, если не изменяет память, тоже фигово работает с иконками. Лучше брать LoadImage из unit Windows. В Canvas нет умных методов, они простые. Если хотите прозрачность используйте что-то другое. Суть вроде в том что сам TBitmap не поддерживает прозрачность. У вас белая кайма вокруг картинки, уверены что это не холст? Вы нарисовали поверх нее !узкую! картинку. Если да, сузьте bitmap через setsize и рисуйте поверх. Мой вариант на данный момент закрашивать хослт под цвет кнопок, чтобы он сливался. Вообще я сейчас сам работаю над проектом где рисую миниатюры на кнопках, там везде проглядывают бока при выделении. Я еще с этим не ковырялся, потом займусь. ---------------------------------------------------------- Упс, только заметил последнее сообщение. Видимо было на другой странице. Ну конечно, маска. Добавляете в ImageList. Я тоже сохраняю все туда. Можете привести кусок кода, который делает маску из финального изображения? Как она делается? Чуствую, придется читать литературу по этой теме. |