Что нового

[Элементы GUI] Быстрая отрисовка элементов Pic при их множественном создании

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Здравствуйте, товарищи.
Имеется следующий вопрос. Можно ли ускорить создание элементов GUICtrlCreatePic?
В приведенном ниже примере при создании таблички 25х25 картинок тратится около 2 секунд на моем компе, что не есть хорошо. Каждая картинка занимает ровно 4,05 кб, они очень маленькие и должны подгружаться быстро, но скорость построения таблицы оставляет желать лучшего, а хотелось бы отображать если и не мгновенно, то побыстрее.

Думал сначала подгружать все картинки в память и объединять в одно полотно с помощью функций GDIPlus, а потом одним махом вывести на экран, но такой вариант не подходит - к каждому элементу должна прикрепляться подсказка.
Подмогните советом, как оптимизировать построение картинки.

Код:
Local $vsize = 25, $hsize = 25
	Dim $a_Map_Ctrl[($hsize * $vsize) + 1]
	$a_Map_Ctrl[0] = $hsize*$vsize

	Global $Form2 = GUICreate("Отображение таблицы", ($hsize * 32) + 32, ($vsize * 32) + 52, 100, 100)
	GUISetOnEvent($GUI_EVENT_CLOSE, "_ShowClose", $Form2)
	Global $SavePic = GUICtrlCreateButton("Сохранить в графическом формате", 16, 28 + ($vsize * 32), ($hsize * 32), 22)
	;GUICtrlSetOnEvent($SavePic, "_ScreenCap")
	GUISetState(@SW_SHOW)

	For $i = 1 To $vsize
		For $j = 1 To $hsize
			$x = StringSplit($a_Map[$i][$j], "|") ; массив $a_Map имеет размерность [$vsize+1][$hsize+1], в каждой ячейке массива хранится: первая часть до "|" - подсказка, вторая - название картинки без расширения
			$c += 1
			$a_Map_Ctrl[$c] = GUICtrlCreatePic(@ScriptDir & "\_graphic\" & $x[$x[0]] & ".bmp", 8 - 16 + ($j * 32), 8 - 32 + ($i * 32), 32, 32)
			If $x[1] Then GUICtrlSetTip($a_Map_Ctrl[$c], $x[1], "Свойства ячейки", 0, 1)
		Next
	Next
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
То ли у меня вопросы сложные, то ли я зря сюда прихожу. За сутки ни одного ответа, эх... :stars:
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
veretragna сказал(а):
За сутки ни одного ответа, эх...
Если в хотите получить быстрый ответ, то почему не выложили рабочий пример? Я, как правило, копирую код с форума, и, если после запуска получаю 100500 ошибок, то просто забиваю на данную тему. Почему я должен править и дописывать ваш код? Лично мне это нафиг не упало. Я это расцениваю как неуважение к другим участникам форума.

В данной ситуации могу посоветовать сначала создавать все ваши Pic элементы пустыми, .bmp считать как bitmap'ы в отдельный массив, а затем вставить их все с помощью STM_SETIMAGE. А еще измерьте, какой процент времени занимает исключительно создание Pic элементов (пустых).

veretragna сказал(а):
То ли у меня вопросы сложные, то ли я зря сюда прихожу.
Ой, вот только не нужно тут сопли разводить. Почему так происходит, я написал выше.


Добавлено:
Сообщение автоматически объединено:

И да, вы создаетет Pic элементы после GUISetState()... Это как минимум еще в 2 раза дольше.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Yashied сказал(а):
Если в хотите получить быстрый ответ, то почему не выложили рабочий пример? Я, как правило, копирую код с форума, и, если после запуска получаю 100500 ошибок, то просто забиваю на данную тему. Почему я должен править и дописывать ваш код? Лично мне это нафиг не упало. Я это расцениваю как неуважение к другим участникам форума.

Если выкладывать рабочий пример с этим кодом, пришлось бы упаковывать рабочий проект с графикой и кучей данных и выкладывать километровую простыню текста, как эту байду использовать. Или вручную делать здоровенный массив данных 25х25, жестко прописать его в скрипте и уже оттуда вызвать, что тоже нехорошо, ибо намного увеличивается объем кода. Поэтому я написал простенькую и наглядную схему работы функции. Да, собственно говоря, там и описывать нечего - два вложенных цикла, которые построчно выводят картинки на форму.
Я не намеревался проявлять неуважение, просто пошел по кратчайшему пути. Извините, если в чем-то не прав.

Yashied сказал(а):
В данной ситуации могу посоветовать сначала создавать все ваши Pic элементы пустыми, .bmp считать как bitmap'ы в отдельный массив, а затем вставить их все с помощью STM_SETIMAGE. А еще измерьте, какой процент времени занимает исключительно создание Pic элементов (пустых).

Вот за это - большое спасибо! Попробую сделать так, о результатах напишу отдельным сообщением.

Yashied сказал(а):


Добавлено:
Сообщение автоматически объединено:

И да, вы создаетет Pic элементы после GUISetState()... Это как минимум еще в 2 раза дольше.




Вот это я сделал специально, чтобы понаблюдать процесс "вживую", т.к. не понимал, почему создание окна с изображением происходит так долго. В проекте сначала создаются все элементы, а уже потом вызывается GUISetState(@SW_SHOW).

Создание элементов с изображениями занимает 1,83 сек, а пустых - 0,15 сек.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
veretragna, ваша задача связана с оптимизацией и требует как минимум запуска кода. Если вы выкладываете полностью рабочий кусок кода, связанный с вашей проблемой, то тем, кто захочет вам помочь, намного проще будет скопировать код, поиграться с ним и вставить его обратно на форум. Естественно, я не хочу видеть всю вашу стену, я хочу видеть ключевые моменты - создание GUI (без лишних элементов), создание Pic элементов и минимальный цикл, плюс в аттаче необходимые картинки. Ну и самое главное, чтобы были прописаны все подключаемые библиотеки и объявлены все массивы и т.д. Это все полне можно сделать за 3-4 мин из любой километровой стены кода.

veretragna сказал(а):
Вот за это - большое спасибо! Попробую сделать так, о результатах напишу отдельным сообщением.
Особо не обольщайтесь, скорее всего все тормоза сведуться именно к созданию элементов.


Добавлено:
Сообщение автоматически объединено:

Как бы то ни было, но 25x25=625 элементов, это много. Если после всех мытарств вы не получите удовлетворительного результата, то советую переходить на GDI+, и объединять все картинки в одно полотно.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Yashied сказал(а):
Особо не обольщайтесь, скорее всего все тормоза сведуться именно к созданию элементов.


Добавлено:
Сообщение автоматически объединено:

Как бы то ни было, но 25x25=625 элементов, это много. Если после всех мытарств вы не получите удовлетворительного результата, то советую переходить на GDI+, и объединять все картинки в одно полотно.




Пока попробую без радикальных изменений, вот как раз переделываю код. В крайнем случае можно сделать так: слить в одно полотно, обрабатывать WM_NOTIFY по коду NM_HOVER и зависимо от координат выводить нужную подсказку. Но тут еще много работы... В любом случае, эксперимент продолжается.


Добавлено:
Сообщение автоматически объединено:

Итак, с подачи товарища Yashied'a получилось решить проблему.
Время отрисовки сократилось более, чем в 8 раз - было 1,87 сек, стало 0,2 сек.
Решил с помощью предварительной загрузки битмапов в память функциями GDIPlus и последующей их отрисовкой STM_SETIMAGE.

Пример - во вложениях.
В зип-архиве лежит собственно скрипт-пример и картинки.
Выдрал на скорую руку из работающего проекта и слегка причесал, поэтому сильно не пинать.
 

Вложения

  • Ex.zip
    118.3 КБ · Просмотры: 13

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Вот это я понимаю подход к проблеме (+1). Рад, что мой совет помог.

P.S

Кстати, GDI+ здесь совсем необязательно использовать, достаточно _WinAPI_LoadBitmap(), если все файлы .bmp.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Yashied сказал(а):
P.S

Кстати, GDI+ здесь совсем необязательно использовать, достаточно _WinAPI_LoadBitmap(), если все файлы .bmp.

В первую очередь пробовал сделать именно так, но почему-то вместо полотна с картинками получилось полотно с одной-единственной отрисованной клеточкой, поэтому на скору руку сделал на рабочем примере с GDIPlus. Попробую еще раз похимичить, с WinAPI должно быть еще быстрее :smile:
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
У вас все (?) .bmp с пустым альфа-каналом сохранены.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Yashied сказал(а):
У вас все (?) .bmp с пустым альфа-каналом сохранены.

Все картинки сохранял программно, вот этим кодом:

Код:
$hImage = _GDIPlus_ImageLoadFromFile($Filename)
	$sCLSID = _GDIPlus_EncodersGetCLSID("BMP")
	$hImage2 = _GDIPlus_ImageResize($hImage, 32, 32)
	_GDIPlus_ImageDispose($hImage)
	FileDelete($Filename)
	_GDIPlus_ImageSaveToFileEx($hImage2, $Filename, $sCLSID)
	_GDIPlus_ImageDispose($hImage2)
	; в оригинале картинка имеет размер 40х40, тут я изменяю ее размер до 32х32 и пишу на то же место


Подскажете, как модифицировать, чтобы сохранять без альфа-канала?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Да воспользуйтесь просто Photoshop. GDI+ по умолчанию использует 32-битный формат, а нужно 24-битный. Что-то там копаться и искать мне уже лень.


Добавлено:
Сообщение автоматически объединено:

И самое главное, если в дальейшем предполагается менять картинки в процессе работы программы, то обязательно освобождайте память, занятую предыдущим битмапом, в противном случае получите классическую утечку памяти. Если по-простому, то так:

Код:
_WinAPI_DeleteObject(GUICtrlSendMsg(...))
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Yashied сказал(а):
Да воспользуйтесь просто Photoshop. GDI+ по умолчанию использует 32-битный формат, а нужно 24-битный. Что-то там копаться и искать мне уже лень.

Нет проблем, покопаю сам.
Моя программа заходит на сайт, сдирает оттуда карту со всеми данными и графикой, подсказки ложит в текстовый файл на хранение, а картинки пережимает в размер 32х32 и ложит себе в кэш, чтобы не скачивать их каждый раз.
Поэтому Photoshop не подходит, надо это дело автоматизировать.
Если найду решение, выложу тут же, а если нет - то и фиг с ним, 4 кб - не критично большой размер для одной пиктограммы, а всего их будет ну максимум 100.


Добавлено:
Сообщение автоматически объединено:

Yashied сказал(а):


Добавлено:
Сообщение автоматически объединено:

И самое главное, если в дальнейшем предполагается менять картинки в процессе работы программы, то обязательно освобождайте память, занятую предыдущим битмапом, в противном случае получите классическую утечку памяти. Если по-простому, то так:

Код:
_WinAPI_DeleteObject(GUICtrlSendMsg(...))





Спасибо, так и сделал. Кстати, уже где-то видел подобную конструкцию.
Картинки меняться не будут, они все при старте программы подгружаются в ассоциативный массив и остаются там в неизменном виде, утечкам по сути взяться негде... Разве что при выполнении GUICtrlSendMsg(...) создается новый объект HBITMAP?
 

Prog

Продвинутый
Сообщения
593
Репутация
73
veretragna [?]
Поэтому Photoshop не подходит, надо это дело автоматизировать.
Во вложении программа которая преобразует в 24 бита все bmp-картинки в выбранной папке.
 

Вложения

  • ImageWithoutAlpha.zip
    13.9 КБ · Просмотры: 6
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Prog сказал(а):
veretragna [?]
Поэтому Photoshop не подходит, надо это дело автоматизировать.
Во вложении программа которая преобразует в 24 бита все bmp-картинки в выбранной папке.

Prog, Ваш метод тоже не совсем то, прибегать к помощи сторонних утилит неохота.
Я уже решил проблему радикально - теперь программа использует PNG вместо битмапов.

Код изменился самую малость:
Код:
$hImage = _GDIPlus_ImageLoadFromFile($Filename)
	$sCLSID = _GDIPlus_EncodersGetCLSID("PNG")
	$hImage2 = _GDIPlus_ImageResize($hImage, 32, 32)
	_GDIPlus_ImageDispose($hImage)
	FileDelete($Filename)
	
	Local $tParams = _GDIPlus_ParamInit(2)
	Local $tData = DllStructCreate("int ColorDepth")
	DllStructSetData($tData, "ColorDepth", 24)
	_GDIPlus_ParamAdd($tParams, $GDIP_EPGCOLORDEPTH, 1, $GDIP_EPTLONG, DllStructGetPtr($tData, "ColorDepth"))
	
	_GDIPlus_ImageSaveToFileEx($hImage2, $Filename, $sCLSID, $tParams)
	_GDIPlus_ImageDispose($hImage2)


Все отлично и быстро работает.
 
Верх