Diamond
09-09-2009, 18:23
Возник интерес создать сложный GDI регион на основе изображения(bitmap), регион создал, но не смотря на все ухищрения, скорость создания региона оставляет желать лучшего.
Пример сканирования:
#include <GuiConstants.au3>
#include <WindowsConstants.au3>
#include <WinApi.au3>
#include <Constants.au3>
Global $hGui, $gdi32dll, $hBmp, $hMemDC, $hOldBmp, $hRgn, $TagBitmap, $tBitmap, $iW, $iH, $Color
$hGui = GUICreate("Test_BitmapInRegion", 200, 200, -1, -1, $WS_POPUPWINDOW)
$gdi32dll = DllOpen("gdi32.dll")
;~ Загружаю bitmap из файла:
$hBmp = _WinAPI_LoadImage(0, "Form.bmp", $IMAGE_BITMAP, 0, 0, $LR_LOADFROMFILE)
;~ Определяю его ширину и высоту:
$TagBitmap = "int bmType;int bmWidth;int bmHeight;int bmWidthBytes;byte bmPlanes;byte bmBitsPixel;ptr bmBits"
$tBitmap = DllStructCreate($TagBitmap)
_WinAPI_GetObject($hBmp, DllStructGetSize($tBitmap), DllStructGetPtr($tBitmap))
$iW = DllStructGetData($tBitmap, 2)
$iH = DllStructGetData($tBitmap, 3)
$tObject = ""
;~ создаю контекст устpойства памяти:
$hMemDC = _WinAPI_CreateCompatibleDC(0)
;~ заменяю bitmap по умолчанию, bitmap'ом загруженным из файла:
$hOldBmp = _WinAPI_SelectObject($hMemDC, $hBmp)
;~ Создаю пустой регион:
$hRgn = _WinAPI_CreateRectRgn(0, 0, 0, 0)
$Color = 0xFFFFFF ; <- цвет на основе которого будут добавляться (или исключаться) области.
;~ Есть два варианта создания региона на основе bitmap, путём добавления к региону($RGN_OR), и путём исключения из него($RGN_DIFF).
;~ В первом варианте создаётся пустой регион к которому впоследствии (в цикле) добавляются нужные нам области.
;~ Во втором варианте создаётся прямоугольный регион идентичный размеру bitmap, из которого будут исключаться лишние области.
;~ Какой из способов предпочтительнее, зависит от размеров региона по отношению к размеру изображения,
;~ если площадь нужного нам региона (визуально) превышает 50% площади изображения, то второй вариант будет работать чуть быстрее.
;~ Формирую регион путём добавления(сканирование "горизонтальными блоками" высотой в 1 пиксель):
Dim $fState, $iXStart, $iX, $iY, $hRgnBlock, $iColor
$hRgnBlock = _WinAPI_CreateRectRgn(0, 0, 0, 0)
For $iY = 0 To $iH
For $iX = 0 To $iW
$iColor = GetPixel($hMemDC, $iX, $iY, $gdi32dll) ; определяю цвет
If $iColor = $Color Then ; если цвет совпадает
If Not $fState Then ; Если цвет на текущей линии (для этого блока) совпал впервые
$iXStart = $iX ;<- запоминаю стартовую позицию.
$fState = True ; <- Отмечаю что сканирование блока было начато
ElseIf $iX = $iW Then ; если достигнут правый край изображения и цвет совпал
SetRectRgn($hRgnBlock, $iXStart, $iY, $iX, $iY+1, $gdi32dll)
_WinAPI_CombineRgn($hRgn, $hRgn, $hRgnBlock, $RGN_OR)
$fState = False ; <- Отмечаю что сканирование текущего блока было завершено
EndIf
ElseIf $fState Then ; если цвет не совпал, но уже совпадал раньше для этого блока(там где мы запоминали стартовую позицию)
SetRectRgn($hRgnBlock, $iXStart, $iY, $iX, $iY+1, $gdi32dll)
_WinAPI_CombineRgn($hRgn, $hRgn, $hRgnBlock, $RGN_OR)
$fState = False ; <- Отмечаю что сканирование текущего блока было завершено
EndIf
Next
Next
_WinAPI_DeleteObject($hRgnBlock)
_WinAPI_SetWindowRgn($hGui, $hRgn, False)
;~ возвращаю старый bitmap:
_WinAPI_SelectObject($hMemDC, $hOldBmp)
;~ освобождаю контекст:
_WinAPI_DeleteDC($hMemDC)
;~ удаляю загруженный bitmap:
_WinAPI_DeleteObject($hBmp)
DllClose($gdi32dll)
GUISetState(@SW_SHOW)
While 1
$msg = GUIGetMsg()
Switch $msg
Case $GUI_EVENT_CLOSE
Exit
EndSwitch
WEnd
Func GetPixel($hDC, $iX, $iY, $gdi32dll="gdi32.dll")
Local $aResult
$aResult = DllCall($gdi32dll, "long", "GetPixel", "hwnd", $hDC, "int", $iX, "int", $iY)
If @error Then Return SetError(@error, 0, 0)
Return SetError(0, 0, $aResult[0])
EndFunc ;==>GetPixel
;~ Преобразует область, в прямоугольную область с указанным pазмеpом.
;~ $hRgn - Идентификатор области.
;~ $X1, $Y1 - Веpхний левый угол пpямоугольной области.
;~ $X2, $Y2 - Нижний пpавый угол пpямоугольной области.
Func SetRectRgn($hRgn, $iX1, $iY1, $iX2, $iY2, $gdi32dll="gdi32.dll")
Local $aResult
$aResult = DllCall($gdi32dll, "int", "SetRectRgn", "hwnd", $hRgn, "int", $iX1, "int", $iY1, "int", $iX2, "int", $iY2)
If @error Then Return SetError(@error, 0, 0)
Return SetError(0, 0, $aResult[0])
EndFunc ;==>SetRectRgnСканировать изображение при каждом запуске скрипта, думаю, плохая идея.
Вопрос: Как сохранить созданный регион в файл, но самое главное как его потом загружать?
P.S. Я знаю что при загрузке и сохранении региона, используются функции GetRegionData и ExtCreateRegion, синтаксис которых мне тоже непонятен.
Пример сканирования:
#include <GuiConstants.au3>
#include <WindowsConstants.au3>
#include <WinApi.au3>
#include <Constants.au3>
Global $hGui, $gdi32dll, $hBmp, $hMemDC, $hOldBmp, $hRgn, $TagBitmap, $tBitmap, $iW, $iH, $Color
$hGui = GUICreate("Test_BitmapInRegion", 200, 200, -1, -1, $WS_POPUPWINDOW)
$gdi32dll = DllOpen("gdi32.dll")
;~ Загружаю bitmap из файла:
$hBmp = _WinAPI_LoadImage(0, "Form.bmp", $IMAGE_BITMAP, 0, 0, $LR_LOADFROMFILE)
;~ Определяю его ширину и высоту:
$TagBitmap = "int bmType;int bmWidth;int bmHeight;int bmWidthBytes;byte bmPlanes;byte bmBitsPixel;ptr bmBits"
$tBitmap = DllStructCreate($TagBitmap)
_WinAPI_GetObject($hBmp, DllStructGetSize($tBitmap), DllStructGetPtr($tBitmap))
$iW = DllStructGetData($tBitmap, 2)
$iH = DllStructGetData($tBitmap, 3)
$tObject = ""
;~ создаю контекст устpойства памяти:
$hMemDC = _WinAPI_CreateCompatibleDC(0)
;~ заменяю bitmap по умолчанию, bitmap'ом загруженным из файла:
$hOldBmp = _WinAPI_SelectObject($hMemDC, $hBmp)
;~ Создаю пустой регион:
$hRgn = _WinAPI_CreateRectRgn(0, 0, 0, 0)
$Color = 0xFFFFFF ; <- цвет на основе которого будут добавляться (или исключаться) области.
;~ Есть два варианта создания региона на основе bitmap, путём добавления к региону($RGN_OR), и путём исключения из него($RGN_DIFF).
;~ В первом варианте создаётся пустой регион к которому впоследствии (в цикле) добавляются нужные нам области.
;~ Во втором варианте создаётся прямоугольный регион идентичный размеру bitmap, из которого будут исключаться лишние области.
;~ Какой из способов предпочтительнее, зависит от размеров региона по отношению к размеру изображения,
;~ если площадь нужного нам региона (визуально) превышает 50% площади изображения, то второй вариант будет работать чуть быстрее.
;~ Формирую регион путём добавления(сканирование "горизонтальными блоками" высотой в 1 пиксель):
Dim $fState, $iXStart, $iX, $iY, $hRgnBlock, $iColor
$hRgnBlock = _WinAPI_CreateRectRgn(0, 0, 0, 0)
For $iY = 0 To $iH
For $iX = 0 To $iW
$iColor = GetPixel($hMemDC, $iX, $iY, $gdi32dll) ; определяю цвет
If $iColor = $Color Then ; если цвет совпадает
If Not $fState Then ; Если цвет на текущей линии (для этого блока) совпал впервые
$iXStart = $iX ;<- запоминаю стартовую позицию.
$fState = True ; <- Отмечаю что сканирование блока было начато
ElseIf $iX = $iW Then ; если достигнут правый край изображения и цвет совпал
SetRectRgn($hRgnBlock, $iXStart, $iY, $iX, $iY+1, $gdi32dll)
_WinAPI_CombineRgn($hRgn, $hRgn, $hRgnBlock, $RGN_OR)
$fState = False ; <- Отмечаю что сканирование текущего блока было завершено
EndIf
ElseIf $fState Then ; если цвет не совпал, но уже совпадал раньше для этого блока(там где мы запоминали стартовую позицию)
SetRectRgn($hRgnBlock, $iXStart, $iY, $iX, $iY+1, $gdi32dll)
_WinAPI_CombineRgn($hRgn, $hRgn, $hRgnBlock, $RGN_OR)
$fState = False ; <- Отмечаю что сканирование текущего блока было завершено
EndIf
Next
Next
_WinAPI_DeleteObject($hRgnBlock)
_WinAPI_SetWindowRgn($hGui, $hRgn, False)
;~ возвращаю старый bitmap:
_WinAPI_SelectObject($hMemDC, $hOldBmp)
;~ освобождаю контекст:
_WinAPI_DeleteDC($hMemDC)
;~ удаляю загруженный bitmap:
_WinAPI_DeleteObject($hBmp)
DllClose($gdi32dll)
GUISetState(@SW_SHOW)
While 1
$msg = GUIGetMsg()
Switch $msg
Case $GUI_EVENT_CLOSE
Exit
EndSwitch
WEnd
Func GetPixel($hDC, $iX, $iY, $gdi32dll="gdi32.dll")
Local $aResult
$aResult = DllCall($gdi32dll, "long", "GetPixel", "hwnd", $hDC, "int", $iX, "int", $iY)
If @error Then Return SetError(@error, 0, 0)
Return SetError(0, 0, $aResult[0])
EndFunc ;==>GetPixel
;~ Преобразует область, в прямоугольную область с указанным pазмеpом.
;~ $hRgn - Идентификатор области.
;~ $X1, $Y1 - Веpхний левый угол пpямоугольной области.
;~ $X2, $Y2 - Нижний пpавый угол пpямоугольной области.
Func SetRectRgn($hRgn, $iX1, $iY1, $iX2, $iY2, $gdi32dll="gdi32.dll")
Local $aResult
$aResult = DllCall($gdi32dll, "int", "SetRectRgn", "hwnd", $hRgn, "int", $iX1, "int", $iY1, "int", $iX2, "int", $iY2)
If @error Then Return SetError(@error, 0, 0)
Return SetError(0, 0, $aResult[0])
EndFunc ;==>SetRectRgnСканировать изображение при каждом запуске скрипта, думаю, плохая идея.
Вопрос: Как сохранить созданный регион в файл, но самое главное как его потом загружать?
P.S. Я знаю что при загрузке и сохранении региона, используются функции GetRegionData и ExtCreateRegion, синтаксис которых мне тоже непонятен.