Что нового

Рисуем картинку на основе данных с массива.

Heler

Знающий
Сообщения
70
Репутация
11
Всем привет! Некоторым образом получаю массив с данными такого вида:
Код:
Dim $aArray[10][20] = _
[[0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], _
 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1], _
 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1]]

По этим данным нужно нарисовать картинку в окне AutoIT.
Ноль - белый пиксель.
Единица - черный пиксель.
1 точка картинки $aArray[0][0] - рисуем белую точку
2 точка картинки $aArray[0][1] - рисуем черную точку
3 точка картинки $aArray[0][2] - рисуем белую точку и т. д.
Посоветуйте пожалуйста как реализовать такое, а также в каком формате лучше сохранять данные с массива в файле, что бы потом максимально быстро, при перезапуске программы, считать их обратно в массив.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Здесь самое узкое место - массив, т.к. его нельзя записать напрямую в файл. Отобразить точки в GUI можно многими способами. Самый простой, и в тоже время самый медленный способ, это использовать функцию GUICtrlCreateGraphic() + GUICtrlSetGraphic(). Наиболее быстрый способ, это отображать точки напрямую в Bitmap, но он не совсем простой (на первый взгляд).

Сохранять точки опять же проще будет в текстовый файл (010111 и т.д.), но последующая обработка этой строки займет слишком много времени. Лучше использовать двоичный файл и записывать в него данные за одно обращение.

Если у тебя размер картинки будет всегда ~20x10 точек, то можешь выбрать любой способ, т.к. для таких размеров скорость вряд ли будет ощутима. Ниже я написал IMHO наиболее быстрый способ решения твоей задачи. Здесь точки записываются в двоичный файл, который имеет следующую структуру (данные по смещению):

0x00 - Ширина изображения, W (4 байта)
0x04 - Высота изображения, H (4 байта)
0x08 - Последовательность точек (цветов), каждое значение - 4 байта, общее количество точек - WxH, общий размер данных - WxHx4 байт.

Код:
#Include <Array.au3>

#Include <GUIConstantsEx.au3>
#Include <Restart.au3>
#Include <WinAPIEx.au3>

Opt('MustDeclareVars', 1)

Global Const $STM_SETIMAGE = 0x0172
Global Const $STM_GETIMAGE = 0x0173

Global Const $sFile = @TempDir & '\dots.dat'

Global $aArray[10][20] = _
	[[0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], _
	 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1], _
	 [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1]]

Global $W = UBound($aArray, 2)
Global $H = UBound($aArray)

Global $tBITMAPINFO, $hFile, $hBitmap, $hPrev, $pBits, $tBits, $iBits, $iBytes
Global $hForm, $Msg, $Pic, $hPic, $Button
Global $tDim = DllStructCreate('dword;dword')
Global $pDim = DllStructGetPtr($tDim)

; Читаем из файла (если существует) размеры изображения
If FileExists($sFile) Then
	$hFile = _WinAPI_CreateFile($sFile, 2, 2)
	_WinAPI_ReadFile($hFile, $pDim, 8, $iBytes)
	$W = DllStructGetData($tDim, 1)
	$H = DllStructGetData($tDim, 2)
EndIf

; Создаем Bitmap
$tBITMAPINFO = DllStructCreate($tagBITMAPINFO)
DllStructSetData($tBITMAPINFO, 'Size', DllStructGetSize($tBITMAPINFO) - 4)
DllStructSetData($tBITMAPINFO, 'Width', $W)
DllStructSetData($tBITMAPINFO, 'Height', $H)
DllStructSetData($tBITMAPINFO, 'Planes', 1)
DllStructSetData($tBITMAPINFO, 'BitCount', 32)
DllStructSetData($tBITMAPINFO, 'Compression', $BI_RGB)
DllStructSetData($tBITMAPINFO, 'SizeImage', 0)
$hBitmap = _WinAPI_CreateDIBSection(0, $tBITMAPINFO, $DIB_RGB_COLORS, $pBits)

; Рисуем точки из файла (если существует) или массива и записываем ее непосредственно в структуру (Bitmap)
$tBits = DllStructCreate('dword[' & $W * $H & ']', $pBits)
$iBits = DllStructGetSize($tBits)
If $hFile Then
	_WinAPI_ReadFile($hFile, $pBits, $iBits, $iBytes)
	_WinAPI_CloseHandle($hFile)
	FileDelete($sFile)
	; Восстанавливаем исходный массив
	Dim $aArray[$H][$W]
	For $y = 0 To $H - 1
		For $x = 0 To $W - 1
			$aArray[$H - $y - 1][$x] = Number(Not DllStructGetData($tBits, 1, $x + ($y * $W) + 1))
		Next
	Next
Else
	For $y = 0 To $H - 1
		For $x = 0 To $W - 1
			DllStructSetData($tBits, 1, (Not $aArray[$H - $y - 1][$x]) * 0xFFFFFF, $x + ($y * $W) + 1)
		Next
	Next
EndIf

; Создаем GUI
GUICreate('MyGUI', 200, 200)
$Pic = GUICtrlCreatePic('', (200 - $W) / 2, (166 - $H) / 2, $W, $H)
$hPic = GUICtrlGetHandle(-1)
$Button = GUICtrlCreateButton('Restart', 65, 166, 70, 23)
GUICtrlSetState(-1, $GUI_DEFBUTTON)

; Устанавливаем Bitmap в GUI (Pic)
_SendMessage($hPic, $STM_SETIMAGE, 0, $hBitmap)
$hPrev = _SendMessage($hPic, $STM_GETIMAGE)
If $hPrev <> $hBitmap Then
	_WinAPI_DeleteObject($hBitmap)
EndIf

GUISetState()

While 1
	$Msg = GUIGetMsg()
	Switch $Msg
		Case $GUI_EVENT_CLOSE
			ExitLoop
		Case $Button
			; Сохраняем данные в двоичный файл и перезапускаем скрипт
			$hFile = _WinAPI_CreateFile($sFile, 1, 4)
			; Записываем размеры ($W и $H) изображения
			DllStructSetData($tDim, 1, $W)
			DllStructSetData($tDim, 2, $H)
			_WinAPI_WriteFile($hFile, $pDim, 8, $iBytes)
			; Записываем информацию о цвете точек
			_WinAPI_WriteFile($hFile, $pBits, $iBits, $iBytes)
			_WinAPI_CloseHandle($hFile)
			_ScriptRestart()
	EndSwitch
WEnd


Restart.au3, WinAPIEx.au3

P.S

Если ты откажешься от использования массива в пользу структуры, то будет вообще все шоколадно.

:smile:
 
Автор
H

Heler

Знающий
Сообщения
70
Репутация
11
Yashied, спасибо за пример и за то что уделили время моей задачке ;)
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
мне кажется, еще быстрее записывать данные сразу в bmp-формате. получишь на выходе картинку. только надо разобраться в структуре. точно уже не помню, но там вначале идет некий заголовок, а дальше просто по 4 байта по 3 байта на цвет и байт на альфа-канал на пиксель.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Kaster сказал(а):
мне кажется, еще быстрее записывать данные сразу в bmp-формате. получишь на выходе картинку. только надо разобраться в структуре. точно уже не помню, но там вначале идет некий заголовок, а дальше просто по 4 байта по 3 байта на цвет и байт на альфа-канал на пиксель.
Если он будет использовать массив, то смысла в этом никакого нет. Все время будет уходить на перечисление его элементов.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
Yashied, Да. Это я неправильно выразился. Да еще и твой пост недочитал. Ты там оказ-ся уже говорил про запись в BMP.
Heler, если интересно то в этой теме начиная вот с этого сообщения разжевана структура BMP-файла и как его записывать - http://autoit-script.ru/index.php?topic=746.msg5321#msg5321
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Kaster, я говорил про запись в Bitmap (HBITMAP), а не в .BMP, но это не важно.Тот файл, который здесь создается, теоретически и есть .bmp, только без заголовка. "Убыстрить" это действительно можно, но с применением GDI+.

Код:
_GDIPlus_Startup()
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
_GDIPlus_ImageSaveToFile($hImage, 'temp.bmp')
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
 
Автор
H

Heler

Знающий
Сообщения
70
Репутация
11
Еще раз привет! Некоторым способом получаю данные которые отображены в примере (переменная $sData). На основе них мне нужно нарисовать картинку.
00 = 0 (черная точка)
01 = 1 (белая точка)
В примере мы получаем картинку 25x15 пикселей. Процесс заполнения структуры из строки выполняется достаточно быстро. А в реальном случае приходится работать с переменными, которые владеют информацией о больших картинках(например 600x700). И данный алгоритм заполнения работает очень медленно. Подскажите пожалуйста как оптимизировать ;)
Код:
#Include <GUIConstantsEx.au3>
#Include <WinAPIEx.au3>

Opt('MustDeclareVars', 1)

Global $sData = _
	'01000101000101010101010001010101010001000100010100'& _
	'01000101000101000100010101010001010001000100010100'& _
	'01010101000101000100010001010001010001000100010100'& _
	'01000101000101000101010101010001010001000100010100'& _
	'01000101000101000101010001010001010001000100010100'& _
	'01000101000101000100010001010001010001000100010100'& _
	'01000101000101000100010101010001010101000100010100'& _
	'01000101000101010100010001010100010001000100010100'& _
	'01010101000101000100010001010001010001000100010100'& _
	'01000101000101000100010001010001010001000100010100'& _
	'01000101000101000100010001010001010001000100010100'& _
	'01010101000101000100010001010001000001000100010100'& _
	'01000101000101000100010001010001010000000100010100'& _
	'01010101000101000100010001010001010001000100010100'& _
	'01000101000101000100010101010101010001000101010101'

Global $W = 25
Global $H = 15

Global Const $STM_SETIMAGE = 0x0172, $STM_GETIMAGE = 0x0173

Global $tBITMAPINFO, $hFile, $hBitmap, $hPrev, $pBits, $tBits, $iBits, $iBytes
Global $hForm, $Msg, $Pic, $hPic, $Button
Global $tDim = DllStructCreate('dword;dword')
Global $pDim = DllStructGetPtr($tDim)

$tBITMAPINFO = DllStructCreate($tagBITMAPINFO)
DllStructSetData($tBITMAPINFO, 'Size', DllStructGetSize($tBITMAPINFO) - 4)
DllStructSetData($tBITMAPINFO, 'Width', $W)
DllStructSetData($tBITMAPINFO, 'Height', $H)
DllStructSetData($tBITMAPINFO, 'Planes', 1)
DllStructSetData($tBITMAPINFO, 'BitCount', 32)
DllStructSetData($tBITMAPINFO, 'Compression', $BI_RGB)
DllStructSetData($tBITMAPINFO, 'SizeImage', 0)
$hBitmap = _WinAPI_CreateDIBSection(0, $tBITMAPINFO, $DIB_RGB_COLORS, $pBits)

$tBits = DllStructCreate('dword[' & $W * $H & ']', $pBits)
$iBits = DllStructGetSize($tBits)

For $i = 1 To 2 * $W * $H Step 2
	DllStructSetData($tBits, 1, StringMid($sData, $i, 2) * 0xFFFFFF, $i + 1 )
Next

GUICreate('MyGUI', 200, 200)
$Pic = GUICtrlCreatePic('', (200 - $W) / 2, (166 - $H) / 2, $W, $H)
$hPic = GUICtrlGetHandle(-1)

_SendMessage($hPic, $STM_SETIMAGE, 0, $hBitmap)
$hPrev = _SendMessage($hPic, $STM_GETIMAGE)
If $hPrev <> $hBitmap Then
    _WinAPI_DeleteObject($hBitmap)
EndIf

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Heler сказал(а):
Подскажите пожалуйста как оптимизировать.
Да особо никак. Откажись от массива в пользу структуры и проблема решится сама собой. Как я уже говорил выше, здесь самое узкое место, это переписывание данных из массива в структуру.

Код:
For $i = 1 To 2 * $W * $H Step 2
    DllStructSetData($tBits, 1, StringMid($sData, $i, 2) * 0xFFFFFF, $i + 1)
Next


Думай.

:-\
 
Верх