Nine Posted August 11, 2021 Share Posted August 11, 2021 (edited) 37 minutes ago, 636C65616E said: that was what i was testing, but it didn't worked, if you have some code #include <WinAPIMem.au3> #include <WinAPIMisc.au3> $t1 = DllStructCreate("dword arr[10]") $t1s = DllStructCreate("byte str[40]", DllStructGetPtr($t1)) _WinAPI_FillMemory(DllStructGetPtr($t1), 10 * 4, 0x01) ConsoleWrite($t1.arr(2) & @CRLF) $t2s = _WinAPI_CopyStruct($t1s) ; successful copy $t2 = DllStructCreate("dword arr[10]", DllStructGetPtr($t2s)) ConsoleWrite($t2.arr(2) & @CRLF) Or another way $t1 = DllStructCreate("dword;word;word[6]") _WinAPI_FillMemory(DllStructGetPtr($t1), DllStructGetSize($t1), 0x01) $t2 = _WinAPI_CopyStruct($t1, "dword;word;word[6]") ConsoleWrite(DllStructGetData($t2, 3, 3) & @CRLF) Edited August 11, 2021 by Nine “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
636C65616E Posted August 11, 2021 Share Posted August 11, 2021 I knew i already tested that ... expandcollapse popupfunc println($smg = '') ConsoleWrite($smg & @CRLF) endfunc #include <GDIPlus.au3> #include <GDIPlusConstants.au3> #include <WinAPIMisc.au3> ; just for testing purpose, you shouldn't deploy with this kind of stuff ; when ragequiting the script, windows should release the process mem and allocated handles (thanks Microsoft) func assert($check, $line = @SCRIPTLINENUMBER) if not $check then MsgBox(0x10, 'ERROR', 'Assert failed at line ' & $line) Exit endif endfunc func GetBPP($type) local $ret = [0,''] switch $type ; $GDIP_PXF01INDEXED ; 1 bpp, indexed ; $GDIP_PXF04INDEXED ; 4 bpp, indexed case $GDIP_PXF08INDEXED $ret[0] = 1 $ret[1] = 'BYTE' case $GDIP_PXF16GRAYSCALE, $GDIP_PXF16RGB555, $GDIP_PXF16RGB565, $GDIP_PXF16ARGB1555 $ret[0] = 2 $ret[1] = 'WORD' ; $GDIP_PXF24RGB ; 24 bpp - 8 bits for each RGB case $GDIP_PXF32RGB, $GDIP_PXF32ARGB, $GDIP_PXF32PARGB $ret[0] = 4 $ret[1] = 'DWORD' case else MsgBox(0x10, 'ERROR', 'Unsupported bpp') Exit endswitch return $ret endfunc func GetRawBitmap($aWinHandle, $type = $GDIP_PXF32RGB) local $bpp = GetBPP($type) local $tmp = WinGetPos($aWinHandle) assert(@ERROR = 0) local $width = $tmp[2] local $height = $tmp[3] local $hDDC = _WinAPI_GetDC($aWinHandle) assert($hDDC <> 0) local $hCDC = _WinAPI_CreateCompatibleDC($hDDC) assert($hCDC <> 0) local $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $width, $height) assert($hBMP <> 0) $tmp = _WinAPI_SelectObject($hCDC, $hBMP) assert($tmp <> 0) $tmp = _WinAPI_PrintWindow($aWinHandle, $hCDC) assert($tmp = True) ; $tmp = _WinAPI_BitBlt($hCDC, 0, 0, $width, $height, $hDDC, 0, 0, 0x00CC0020) ; assert($tmp = True) local $BMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) assert(@ERROR = 0) ;~ println('width = ' & $width) ;~ println('height = ' & $height) local $data = _GDIPlus_BitmapLockBits($BMP, 0, 0, $width, $height, $GDIP_ILMREAD, $type) assert(@ERROR = 0) ;~ println('data.width = ' & $data.Width) ;~ println('data.height = ' & $data.Height) ;~ println('data.stride = ' & $data.Stride) ;~ println('width * bpp = ' & $width * $bpp[0]) $tmp = DllStructCreate($bpp[1] & '[' & ($width * $height) & ']', $data.Scan0) assert(@ERROR = 0) local $ind = 0 for $i = 1 to $width * $height local $x = DllStructGetData($tmp, 1, $i) if $x > 0 then println('A[' & $i-1 & '] = 0x' & hex($x,6)) $ind = $i ExitLoop endif next local $raw = _WinAPI_CopyStruct($tmp, $bpp[1] & '[' & ($width * $height) & ']') assert(@ERROR = 0) println('B[' & $ind-1 & '] = 0x' & hex(DllStructGetData($raw,1,$ind),6)) ; <<<< NOT WORKING ! _GDIPlus_BitmapUnlockBits($BMP, $data) assert(@ERROR = 0) ; a good practice: dispose/release/free objects in reverse order of instantiation/allocation, as it is common one may rely on a precedent ; no assert on release because w/e _GDIPlus_BitmapDispose($BMP) _WinAPI_DeleteObject($hBMP) _WinAPI_DeleteDC($hCDC) _WinAPI_ReleaseDC($aWinHandle, $hDDC) ; to keep ref and prevent deallocation of the raw buffer local $ret = [$width,$height,$raw,$ind] return $ret endfunc func GetPixel(byref $raw, $x, $y) ; , $line = @SCRIPTLINENUMBER) ; assert(0 <= $y and $y < $raw[1], $line) ; assert(0 <= $x and $x < $raw[0], $line) local $ind = $y * $raw[0] + $x ; this is a normalized/flattened array local $ret = DllStructGetData($raw[2], 1, 1 + $ind) assert(@ERROR = 0) return $ret endfunc ; if not _WinAPI_DwmIsCompositionEnabled() then ; MsgBox(0x10, 'ERROR', 'DWM not enabled') ; Exit ; endif _GDIPlus_Startup() local $pid = Run('notepad.exe', '') local $hwnd = WinWait('[CLASS:Notepad]', '', 10) local $time = TimerInit() local $raw = GetRawBitmap($hwnd) ConsoleWrite('GetRawBitmap exec time: ' & Round(TimerDiff($time)) & ' ms' & @CRLF) println('C[' & $raw[3]-1 & '] = 0x' & hex(DllStructGetData($raw[2],1,$raw[3]),6)) ProcessClose($pid) _GDIPlus_Shutdown() this is not working, And anyway the stride still need to be handled Link to comment Share on other sites More sharing options...
636C65616E Posted August 11, 2021 Share Posted August 11, 2021 (edited) so after inspecting the memory in detail, it happens that the allocated buffer is far smaller than it should be, so ... i have no idea what's going on now edit: i think i got what's the problem, i remember old stuff from a long time ago about bmp files hehe got it ! it was about the stride, bmp files are usually indexed in bottom to top: local $data = _GDIPlus_BitmapLockBits($BMP, 0, 0, $width, $height, $GDIP_ILMREAD, $type) assert(@ERROR = 0) local $str = $bpp[1] & '[' & ($width * $height) & ']' local $raw = DllStructCreate($str) if $data.stride > 0 then _WinAPI_MoveMemory(DllStructGetPtr($raw), $data.Scan0, DllStructGetSize($raw)) assert(@ERROR = 0) else local $offset = $width * $bpp[0] ; = abs($data.stride) local $dst = DllStructGetPtr($raw) local $src = $data.Scan0 for $y = 1 to $height _WinAPI_MoveMemory($dst, $src, $offset) assert(@ERROR = 0) $dst += $offset $src -= $offset next endif $tmp = _GDIPlus_BitmapUnlockBits($BMP, $data) assert($tmp = True) also for non dyadic data (24bpp, 1bpp, 4bpp), the buffer contains padding bytes, should be handled too (i'm not 100% sure about that) Edited August 11, 2021 by 636C65616E Link to comment Share on other sites More sharing options...
Nine Posted August 11, 2021 Share Posted August 11, 2021 (edited) rtlmovememory is the fastest way I found so far. Winapi_CopyStruct is based on it AND it is working perfectly fine...Continue working. Just a hint : with win10 use ARGB. I may provide in near future an UDF on this. Going AFK for a few days. Then back, I'll see... Do you have a fast way to search into an array struct of dword for a specific DWORD ? Edited August 11, 2021 by Nine “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
636C65616E Posted August 11, 2021 Share Posted August 11, 2021 (edited) rtlmovememory wasn't accessible from the user mode when i tested (looks weird as it is stated on msdn that it is on ntdll (for usermode)) to search into a raw buffer, well usually you do that on language 'oriented' to it, but in AutoIt i usually do some regexp for pattern matching anyway you can still run some batch of asm (done some tests few days ago), just hard code in asm the pattern finder and call it with a dllcall in autoit, gonna send some code later on, need to find where i put this stuff haha, i guess this will be the fastest way: calling from autoit is slow, but the no allocation and really fast asm instruction may finally compensate EDIT: @Nine here are some idea, ofc just change the asm to handle w/e, i wrote that in 5 mins so guess it's not optimal at all >> CDECL 0: 57 push edi 1: 53 push ebx 2: 8b 5c 24 0c mov ebx,DWORD PTR [esp+0xc] 6: 8b 4c 24 10 mov ecx,DWORD PTR [esp+0x10] a: 8b 54 24 14 mov edx,DWORD PTR [esp+0x14] e: 01 ca add edx,ecx 10: 31 c0 xor eax,eax 00000012 <MatchLoop>: 12: 8d 3c 81 lea edi,[ecx+eax*4] 15: 39 d7 cmp edi,edx 17: 7d 07 jge 20 <NotFound> 19: 3b 1f cmp ebx,DWORD PTR [edi] 1b: 74 06 je 23 <Return> 1d: 40 inc eax 1e: eb f2 jmp 12 <MatchLoop> 00000020 <NotFound>: 20: 83 c8 ff or eax,0xffffffff 00000023 <Return>: 23: 5b pop ebx 24: 5f pop edi 25: c3 ret NB: we don't need to align the <MatchLoop> as the whole loop is included in the 16b cache from 00000010 expandcollapse popup; Here a little snippet i use to align and npad stuff inside chunks of asm ; #TOSEE: disgusting string convertion, StringReplace and string concatenation ... but w/e those binaries should be quite small ; maybe could use StringRegExReplace func _OptimizeCallable($bin) ; for x86 we ALIGN 16 $bin = Binary($bin) ; to ensure Binary local $len = BinaryLen($bin) local $struct = DllStructCreate('BYTE b[' & (15 + $len) & ']') local $base = DllStructGetPtr($struct) local $offset = Number(BitAND($base + 15, BitNOT(15)) - $base) $bin = StringTrimLeft(String($bin),2) ; replace nop x by conservative instructions ; Intel recommandation: some kernel code assume those are atomic $bin = StringReplace($bin, '9090909090909090', '0F1F840000000000') ; 8: nop d[eax+eax+0x0] (+32b) $bin = StringReplace($bin, '90909090909090' , '0F1F8000000000' ) ; 7: nop d[eax+0x0] (+32b) $bin = StringReplace($bin, '909090909090' , '660F1F440000' ) ; 6: nop w[eax+eax+0x0] (+08b) $bin = StringReplace($bin, '9090909090' , '0F1F440000' ) ; 5: nop d[eax+eax+0x0] (+08b) $bin = StringReplace($bin, '90909090' , '0F1F4000' ) ; 4: nop d[eax+0x0] (+08b) $bin = StringReplace($bin, '909090' , '0F1F00' ) ; 3: nop d[eax] $bin = StringReplace($bin, '9090' , '6690' ) ; 2: ops nop | xchg ax,ax ; add padding bytes before (nop) and after (int3) for $i = 1 to $offset $bin = '90' & $bin ; nop < to avoid an INT3 if we call between base and func addr next for $i = 1 to (15-$offset) $bin = $bin & 'CC' ; INT3 < must be a RET or JMP/JCC before so if it happens = malformed next $struct.b = Binary('0x' & $bin) local $ret = [$struct,$base+$offset] return $ret endfunc global $FindDowrdAsm = null global $FindDowrdAsm_ptr = null func InitAsm() local $res = _OptimizeCallable('0x57538B5C240C8B4C24108B54241401CA31C08D3C8139D77D073B1F740640EBF283C8FF5B5FC3') $FindDowrdAsm = $res[0] $FindDowrdAsm_ptr = $res[1] endfunc func FindDwordAsm($aStruct, $aValue) local $res = DllCallAddress('DWORD:cdecl', $FindDowrdAsm_ptr, 'DWORD', $aValue, 'DWORD', DllStructGetPtr($aStruct), 'DWORD', DllStructGetSize($aStruct)) return $res[0] endfunc func FindDword($aStruct, $aValue) local $size = Floor(DllStructGetSize($aStruct) / 4) for $i = 1 to $size if (DllStructGetData($aStruct, 1, $i) = $aValue) then return ($i - 1) endif next return (-1) endfunc $data = DllStructCreate('DWORD D[1024]') $pos = 950 $data.D(1 + $pos) = 0x10 InitAsm() $nb = 1000 global $arr = [ [0,0] , [0,0] ] for $i = 1 to $nb $time = TimerInit() FindDwordAsm($data, 0x10) $time = TimerDiff($time) $arr[0][0] += $time $arr[0][1] += $time * $time $time = TimerInit() FindDword($data, 0x10) $time = TimerDiff($time) $arr[1][0] += $time $arr[1][1] += $time * $time next $arr[0][0] /= $nb $arr[0][1] = Sqrt( $arr[0][1] / $nb - $arr[0][0] * $arr[0][0] ) $arr[1][0] /= $nb $arr[1][1] = Sqrt( $arr[1][1] / $nb - $arr[1][0] * $arr[1][0] ) ; mean stdev println(StringFormat('Asm : %.4f | %.4f', $arr[0][0], $arr[0][1])) println(StringFormat('Naive : %.4f | %.4f', $arr[1][0], $arr[1][1])) The output: Asm : 0.0175 | 0.0075 Naive : 1.6179 | 0.5095 Even with 1000 calls the avg may significantly vary, but the acceleration magnitude is always about a factor 100 on my computer. Ofc the code is not really safe, doesn't handle error, can fall in segfault, etc ; but it's just about a proof of concept Edited August 17, 2021 by 636C65616E Loc 1 Link to comment Share on other sites More sharing options...
Loc Posted August 12, 2021 Author Share Posted August 12, 2021 I get the coordinates and color with au3info but when I apply to the code the returned color is different from the one obtained in au3info expandcollapse popup#include <GDIPlus.au3> #include <GDIPlusConstants.au3> #include <WinAPIMisc.au3> ; just for testing purpose, you shouldn't deploy with this kind of stuff ; when ragequiting the script, windows should release the process mem and allocated handles (thanks Microsoft) func assert($check, $line = @SCRIPTLINENUMBER) if not $check then MsgBox(0x10, 'ERROR', 'Assert failed at line ' & $line) Exit endif endfunc func GetBPP($type) switch $type ; $GDIP_PXF01INDEXED ; 1 bpp, indexed ; $GDIP_PXF04INDEXED ; 4 bpp, indexed case $GDIP_PXF08INDEXED return 'BYTE' case $GDIP_PXF16GRAYSCALE, $GDIP_PXF16RGB555, $GDIP_PXF16RGB565, $GDIP_PXF16ARGB1555 return 'WORD' ; $GDIP_PXF24RGB ; 24 bpp - 8 bits for each RGB case $GDIP_PXF32RGB, $GDIP_PXF32ARGB, $GDIP_PXF32PARGB return 'DWORD' case else MsgBox(0x10, 'ERROR', 'Unsupported bpp') Exit endswitch endfunc func GetRawBitmap($aWinHandle, $type = $GDIP_PXF32RGB) local $str = GetBPP($type) local $tmp = WinGetPos($aWinHandle) assert(@ERROR = 0) local $width = $tmp[2] local $height = $tmp[3] local $hDDC = _WinAPI_GetDC($aWinHandle) assert($hDDC <> 0) local $hCDC = _WinAPI_CreateCompatibleDC($hDDC) assert($hCDC <> 0) local $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $width, $height) assert($hBMP <> 0) $tmp = _WinAPI_SelectObject($hCDC, $hBMP) assert($tmp <> 0) $tmp = _WinAPI_PrintWindow($aWinHandle, $hCDC) assert($tmp = True) $tmp = _WinAPI_BitBlt($hCDC, 0, 0, $width, $height, $hDDC, 0, 0, 0x00CC0020) assert($tmp = True) local $BMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) assert(@ERROR = 0) local $data = _GDIPlus_BitmapLockBits($BMP, 0, 0, $width, $height, $GDIP_ILMREAD, $type) assert(@ERROR = 0) $str &= '[' & ($width * $height) & ']' $tmp = DllStructCreate($str, $data.Scan0) ; weirdly zero memory ; local $raw = _WinAPI_CopyStruct($tmp, $str) ; assert(@ERROR = 0) local $raw = DllStructCreate($str) DllStructSetData($raw, 1, Binary(DllStructGetData($tmp, 1))) _GDIPlus_BitmapUnlockBits($BMP, $data) assert(@ERROR = 0) ; a good practice: dispose/release/free objects in reverse order of instantiation/allocation, as it is common one may rely on a precedent ; no assert on release because w/e _GDIPlus_BitmapDispose($BMP) _WinAPI_DeleteObject($hBMP) _WinAPI_DeleteDC($hCDC) _WinAPI_ReleaseDC($aWinHandle, $hDDC) ; to keep ref and prevent deallocation of the raw buffer local $ret = [$width,$height,$raw] return $ret endfunc func GetPixel(byref $raw, $x, $y) ; , $line = @SCRIPTLINENUMBER) ; assert(0 <= $y and $y < $raw[1], $line) ; assert(0 <= $x and $x < $raw[0], $line) local $ind = $y * $raw[0] + $x ; this is a normalized/flattened array return DllStructGetData($raw[2], 1, 1 + $ind) endfunc Func _Pixelcolor($hwnd, $x, $y) Local $raw = GetRawBitmap($hwnd) Return '0x' & Hex(GetPixel($raw, $x, $y),6) EndFunc if not _WinAPI_DwmIsCompositionEnabled() then MsgBox(0x10, 'ERROR', 'DWM not enabled') Exit endif _GDIPlus_Startup() Local $hw = WinGetHandle('NumberApp') MsgBox(0, 0, _Pixelcolor($hw, 39, 48)) _GDIPlus_Shutdown() Link to comment Share on other sites More sharing options...
Solution 636C65616E Posted August 12, 2021 Solution Share Posted August 12, 2021 (edited) The code you posted doesnt work as @Nine and I were discussing, because of how the BMP data are indexed. This one works, the GetRawBitmap is pretty slow (about 60 ms for my test case when the FetchColors takes about 20, basically it will depend on the height of your image) because we reindex the data (by moving each chunk one by one), if you want it to be fast it is possible to don't move them, and just access with the correct indexing regarding the stride. expandcollapse popup#include <GDIPlus.au3> #include <GDIPlusConstants.au3> #include <WinAPIMisc.au3> func println($smg = '') ConsoleWrite($smg & @CRLF) endfunc ; just for testing purpose, you shouldn't deploy with this kind of stuff ; when ragequiting the script, windows should release the process mem and allocated handles (thanks Microsoft) func assert($check, $line = @SCRIPTLINENUMBER) if not $check then MsgBox(0x10, 'ERROR', 'Assert failed at line ' & $line) Exit endif endfunc global $OX = [5, 10, 15, 20, 25] global $OY = [7, 11, 13, 17, 19] func FetchColors($aWinHandle) local $tmp = WinGetPos($aWinHandle) assert(@ERROR = 0) local $width = $tmp[2] local $height = $tmp[3] local $hDDC = _WinAPI_GetDC($aWinHandle) assert($hDDC <> 0) local $hCDC = _WinAPI_CreateCompatibleDC($hDDC) assert($hCDC <> 0) local $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $width, $height) assert($hBMP <> 0) $tmp = _WinAPI_SelectObject($hCDC, $hBMP) assert($tmp <> 0) $tmp = _WinAPI_PrintWindow($aWinHandle, $hCDC) assert($tmp = True) ; $tmp = _WinAPI_BitBlt($hCDC, 0, 0, $width, $height, $hDDC, 0, 0, 0x00CC0020) ; assert($tmp = True) local $BMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) assert(@ERROR = 0) local $ret = ObjCreate('scripting.dictionary') for $x in $OX for $y in $OY local $key = $x & ':' & $y local $val = _GDIPlus_BitmapGetPixel($BMP,$x,$y) assert(@ERROR = 0) ; println('[' & StringFormat('%02d:%02d',$x,$y) & '] 0x' & hex($val,6)) $ret.Add($key,$val) next next _GDIPlus_BitmapDispose($BMP) _WinAPI_DeleteObject($hBMP) _WinAPI_ReleaseDC($aWinHandle, $hDDC) _WinAPI_DeleteDC($hCDC) return $ret endfunc func GetBPP($type) local $ret = [0,''] switch $type ; $GDIP_PXF01INDEXED ; 1 bpp, indexed ; $GDIP_PXF04INDEXED ; 4 bpp, indexed case $GDIP_PXF08INDEXED $ret[0] = 1 $ret[1] = 'BYTE' case $GDIP_PXF16GRAYSCALE, $GDIP_PXF16RGB555, $GDIP_PXF16RGB565, $GDIP_PXF16ARGB1555 $ret[0] = 2 $ret[1] = 'WORD' ; $GDIP_PXF24RGB ; 24 bpp - 8 bits for each RGB case $GDIP_PXF32RGB, $GDIP_PXF32ARGB, $GDIP_PXF32PARGB $ret[0] = 4 $ret[1] = 'DWORD' case else MsgBox(0x10, 'ERROR', 'Unsupported bpp') Exit endswitch return $ret endfunc func GetRawBitmap($aWinHandle, $type = $GDIP_PXF32RGB) local $bpp = GetBPP($type) local $tmp = WinGetPos($aWinHandle) assert(@ERROR = 0) local $width = $tmp[2] local $height = $tmp[3] local $hDDC = _WinAPI_GetDC($aWinHandle) assert($hDDC <> 0) local $hCDC = _WinAPI_CreateCompatibleDC($hDDC) assert($hCDC <> 0) local $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $width, $height) assert($hBMP <> 0) $tmp = _WinAPI_SelectObject($hCDC, $hBMP) assert($tmp <> 0) $tmp = _WinAPI_PrintWindow($aWinHandle, $hCDC) assert($tmp = True) ; $tmp = _WinAPI_BitBlt($hCDC, 0, 0, $width, $height, $hDDC, 0, 0, 0x00CC0020) ; assert($tmp = True) local $BMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) assert(@ERROR = 0) local $data = _GDIPlus_BitmapLockBits($BMP, 0, 0, $width, $height, $GDIP_ILMREAD, $type) assert(@ERROR = 0) local $raw = DllStructCreate($bpp[1] & '[' & ($width * $height) & ']') local $row_size = $width * $bpp[0] if ($data.stride < 0 or $data.stride <> $row_size) then local $dst = DllStructGetPtr($raw) local $src = $data.Scan0 for $y = 1 to $height _WinAPI_MoveMemory($dst, $src, $row_size) assert(@ERROR = 0) $dst += $row_size $src += $data.stride next else _WinAPI_MoveMemory(DllStructGetPtr($raw), $data.Scan0, DllStructGetSize($raw)) assert(@ERROR = 0) endif $tmp = _GDIPlus_BitmapUnlockBits($BMP, $data) assert($tmp = True) ; a good practice: dispose/release/free objects in reverse order of instantiation/allocation, as it is common one may rely on a precedent ; no assert on release because w/e _GDIPlus_BitmapDispose($BMP) _WinAPI_DeleteObject($hBMP) _WinAPI_ReleaseDC($aWinHandle, $hDDC) _WinAPI_DeleteDC($hCDC) ; to keep ref and prevent deallocation of the raw buffer local $ret = [$width,$height,$raw] return $ret endfunc func GetPixel(byref $raw, $x, $y) ; , $line = @SCRIPTLINENUMBER) ; assert(0 <= $y and $y < $raw[1], $line) ; assert(0 <= $x and $x < $raw[0], $line) local $ind = $y * $raw[0] + $x ; this is a normalized/flattened array return DllStructGetData($raw[2], 1, 1 + $ind) endfunc ; if not _WinAPI_DwmIsCompositionEnabled() then ; MsgBox(0x10, 'ERROR', 'DWM not enabled') ; Exit ; endif _GDIPlus_Startup() $pid = Run('notepad.exe', '') $hwnd = WinWait('[CLASS:Notepad]', '', 10) $time = TimerInit() $raw = GetRawBitmap($hwnd) $time = Round(TimerDiff($time)) println('GetRawBitmap exec time: ' & $time & ' ms') $time = TimerInit() local $dic = FetchColors($hwnd) $time = Round(TimerDiff($time)) println('FetchColors exec time: ' & $time & ' ms') $test = True for $y in $OY for $x in $OX if not $dic.Item($x & ':' & $y) = GetPixel($raw,$x,$y) then $test = False ExitLoop 2 endif next next println('Check = ' & $test) ProcessClose($pid) _GDIPlus_Shutdown() EDIT1: ofc getting the raw bitmap will increase your memory complexity as it will allocate the whole bmp buffer, but you can access w/e coords you want after EDIT2: On 8/12/2021 at 10:06 AM, Loc said: Func _Pixelcolor($hwnd, $x, $y) Local $raw = GetRawBitmap($hwnd) Return '0x' & Hex(GetPixel($raw, $x, $y),6) EndFunc On 8/10/2021 at 10:33 PM, 636C65616E said: In a more general way, and that's valid in nearly any programming case and based on some elementary logic: if the data you want to test are constant on some period of time, compute them once. Same remark, fetch the array once and check after: each time you're calling _PixelColor you will fetch the whole bitmap, that's pointless, the idea behind getting the whole bitmap is exactly to avoid doing that: what's costy here is all the stuff you do to get the bitmap (create hbmp, dc, compatible dc, print, etc) so you want to do it once ! EDIT3: also GetPixel returns a RGB value, regardless of the actual bmp pixel format ... Edited August 13, 2021 by 636C65616E Loc 1 Link to comment Share on other sites More sharing options...
Loc Posted August 12, 2021 Author Share Posted August 12, 2021 Find 1 time and compare them then I solved that problem. I saw 2 people discussing so I thought I could edit and improve my original _Getcolor function to make it search faster and remove redundant functions Link to comment Share on other sites More sharing options...
636C65616E Posted August 13, 2021 Share Posted August 13, 2021 Hey, I slightly modified GetRawBitmap in my last post to handle padding bytes. Did you finally got a satisfying solution / answer ? Link to comment Share on other sites More sharing options...
Loc Posted August 14, 2021 Author Share Posted August 14, 2021 Thank you for your kind help Link to comment Share on other sites More sharing options...
Loc Posted August 17, 2021 Author Share Posted August 17, 2021 (edited) Vào ngày 12/8/2021 lúc 17:30, 636C65616E cho biết: Mã bạn đã đăng không hoạt động như @Chín và tôi đã thảo luận vì cách lập chỉ mục dữ liệu BMP. Cái này hoạt động, GetRawBitmap khá chậm (khoảng 60 mili giây đối với trường hợp thử nghiệm của tôi khi FetchColors mất khoảng 20, về cơ bản nó sẽ phụ thuộc vào chiều cao của hình ảnh của bạn) vì chúng tôi lập chỉ mục dữ liệu (bằng cách di chuyển từng đoạn một), nếu bạn muốn nó nhanh, có thể không di chuyển chúng và chỉ cần truy cập với lập chỉ mục chính xác liên quan đến bước tiến. EDIT1: ofc lấy bitmap thô sẽ làm tăng độ phức tạp bộ nhớ của bạn vì nó sẽ phân bổ toàn bộ bộ đệm bmp, nhưng bạn có thể truy cập các coords w / e mà bạn muốn sau đó CHỈNH SỬA2: Same remark, fetch the array once and check after: each time you're calling _PixelColor you will fetch the whole bitmap, that's pointless, the idea behind getting the whole bitmap is exactly to avoid doing that: what's costy here is all the stuff you do to get the bitmap (create hbmp, dc, compatible dc, print, etc) so you want to do it once ! EDIT3: also GetPixel returns a RGB value, regardless of the actual bmp pixel format ... Since my data is subject to change, it is not possible to find and load the array once. now it takes me more than 1 second to get the result. Can you give me an example to find once and compare them with my first code 😰 Edited August 17, 2021 by Loc Link to comment Share on other sites More sharing options...
636C65616E Posted August 17, 2021 Share Posted August 17, 2021 1 minute ago, Loc said: Since my data is subject to change, it is not possible to find and load the array once. now it takes me more than 1 second to get the result When you compare your data (all your 'if' in your first code): here you don't need to reload the array. Each time you're doing all your 'if' load once. You data may change, but as you start to compare stuff, you need to cache them (there's no meaning to compare data from differents renders in your case). Link to comment Share on other sites More sharing options...
Loc Posted August 17, 2021 Author Share Posted August 17, 2021 Can anyone help me with the old code? I feel so confused 😭 Link to comment Share on other sites More sharing options...
636C65616E Posted August 17, 2021 Share Posted August 17, 2021 (edited) Also there's some redundancies in you checks, but i don't have time to explore and fix it for you (some tests are the same from one number to another, you should do them once, instead of what your doing, anyway it's a matter of maybe 0.5ms to redo them so ... -_-, just to makes stuff more readable) expandcollapse popup#include <GDIPlus.au3> #include <GDIPlusConstants.au3> #include <WinAPIMisc.au3> #include <Array.au3> func assert($check, $line = @SCRIPTLINENUMBER) if not $check then MsgBox(0x10, 'ERROR', 'Assert failed at line ' & $line) Exit endif endfunc func FetchColors($aWinHandle, byref $OX, byref $OY) local $tmp = WinGetPos($aWinHandle) assert(@ERROR = 0) local $width = $tmp[2] local $height = $tmp[3] local $hDDC = _WinAPI_GetDC($aWinHandle) assert($hDDC <> 0) local $hCDC = _WinAPI_CreateCompatibleDC($hDDC) assert($hCDC <> 0) local $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $width, $height) assert($hBMP <> 0) $tmp = _WinAPI_SelectObject($hCDC, $hBMP) assert($tmp <> 0) $tmp = _WinAPI_PrintWindow($aWinHandle, $hCDC) assert($tmp = True) ; $tmp = _WinAPI_BitBlt($hCDC, 0, 0, $width, $height, $hDDC, 0, 0, 0x00CC0020) ; assert($tmp = True) local $BMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) assert(@ERROR = 0) local $ret = ObjCreate('scripting.dictionary') for $x in $OX for $y in $OY local $key = $x & ':' & $y if $ret.Exists($key) then ContinueLoop local $val = _GDIPlus_BitmapGetPixel($BMP,$x,$y) assert(@ERROR = 0) ; println('[' & StringFormat('%02d:%02d',$x,$y) & '] 0x' & hex($val,6)) $ret.Add($key,$val) next next _GDIPlus_BitmapDispose($BMP) _WinAPI_DeleteObject($hBMP) _WinAPI_ReleaseDC($aWinHandle, $hDDC) _WinAPI_DeleteDC($hCDC) return $ret endfunc func _GetColor(byref $cols, $x, $y) local $key = $x & ':' & $y if not $cols.Exists($key) then MsgBox(0x10, 'error', StringFormat('Coords (%d,%d) not fetched', $x, $y)) Exit endif return $cols.Item($key) endfunc Func _RoomNumber(byref $cols, byref $OX, byref $OY) ; local $cols = FetchColors($hwnd, $OX, $OY) local $X0Y0 = _GetColor($cols, $OX[0], $OY[0]) local $X1Y0 = _GetColor($cols, $OX[1], $OY[0]) local $X2Y0 = _GetColor($cols, $OX[2], $OY[0]) local $X2Y2 = _GetColor($cols, $OX[2], $OY[2]) local $X2Y4 = _GetColor($cols, $OX[2], $OY[4]) local $X0Y1 = _GetColor($cols, $OX[0], $OY[1]) local $X0Y2 = _GetColor($cols, $OX[0], $OY[2]) local $X0Y3 = _GetColor($cols, $OX[0], $OY[3]) local $X0Y4 = _GetColor($cols, $OX[0], $OY[4]) local $X2Y1 = _GetColor($cols, $OX[2], $OY[1]) local $X1Y2 = _GetColor($cols, $OX[1], $OY[2]) local $X2Y3 = _GetColor($cols, $OX[2], $OY[3]) local $X1Y4 = _GetColor($cols, $OX[1], $OY[4]) If $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 <> 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 = 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 0'); Number 0 ElseIf $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 <> 0xFFFF00 And $X2Y1 <> 0xFFFF00 And _ $X0Y2 <> 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 <> 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 <> 0xFFFF00 And _ $X0Y4 <> 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 <> 0xFFB400 Then MsgBox(0, '', 'Number 1'); Number 1 ElseIf $X0Y0 = 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 <> 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 = 0xFFFF00 And $X2Y3 <> 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 2'); Number 2 ElseIf $X0Y0 = 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 <> 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 <> 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 3'); Number 3 ElseIf $X0Y0 = 0xFFFF00 And $X0Y0 <> 0xFFFF00 And $X2Y0 = 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 <> 0xFFB400 And $X1Y4 <> 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 4'); Number 4 ElseIf $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 = 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 <> 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 5'); Number 5 ElseIf $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 <> 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 <> 0xFFFF00 And _ $X0Y3 = 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 6'); Number 6 ElseIf $X0Y0 = 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 <> 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 <> 0xFFFF00 And $X1Y2 <> 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 <> 0xFFB400 And $X1Y4 <> 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 7'); Number 7 ElseIf $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 <> 0xFFFF00 And _ $X0Y3 = 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 8'); Number 8 ElseIf $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 And $X2Y0 <> 0xFFFF00 And _ $X0Y1 = 0xFFFF00 And $X2Y1 = 0xFFFF00 And _ $X0Y2 = 0xFFFF00 And $X1Y2 = 0xFFFF00 And $X2Y2 = 0xFFFF00 And _ $X0Y3 <> 0xFFFF00 And $X2Y3 = 0xFFFF00 And _ $X0Y4 = 0xFFB400 And $X1Y4 = 0xFFB400 And $X2Y4 = 0xFFB400 Then MsgBox(0, '', 'Number 9'); Number 9 ElseIf $X0Y0 = 0xFFFFFF And $X0Y0 = 0xFFFFBD And $X2Y0 = 0xFFFFAF And _ $X0Y4 = 0x00DB00 And $X1Y4 = 0x00DB00 And $X2Y4 = 0x00B500 Then MsgBox(0, '', 'String S');Chu~ S ; Else ? EndIf; Number 0 EndFunc $handle = WinGetHandle('NumberApp') Global $OX1[3] = [32, 35, 39], $OX2[3] = [45, 48, 52], $OX3[3] = [58, 61, 65], $OY[5] = [69, 71, 73, 75, 77] global $TotX = [32, 35, 39, 45, 48, 52, 58, 61, 65] ; just OX1, OX2 and OX3 concatenated ; DO THIS COMPLETE BLOCK EACH TIME YOU WANT TO GET THE 'NUMBERS' ; START $cols = FetchColors($handle, $TotX, $OY) _RoomNumber($cols, $OX1, $OY) _RoomNumber($cols, $OX2, $OY) _RoomNumber($cols, $OX3, $OY) ; END EDIT: for what i think you're trying to do, you should get the whole screen one time and retrieve the 3 numbers with the same screen. ofc if there's multiple call to this kind of stuff, you could alloc the gdi objects onces and update thems each time instead of allocating and freeing all the time, etc. Optimization depends on your code, it's behavior and what you are trying to do, with this little overview of what you are doing, i can't do more, sorry ... Optimizing a code is achieved by otpimising the 'what' i'm doing and also the 'how' i'm doing it, so ... Edited August 17, 2021 by 636C65616E Loc 1 Link to comment Share on other sites More sharing options...
Loc Posted August 18, 2021 Author Share Posted August 18, 2021 thank you very much. Right now it's early morning with me, have a nice day 😊 Link to comment Share on other sites More sharing options...
markyrocks Posted August 18, 2021 Share Posted August 18, 2021 (edited) I figure that i should point out that in the last example posted there appears to be some typos. Something like the following could never evaluate to true. If $X0Y0 <> 0xFFFF00 And $X0Y0 = 0xFFFF00 I was taking a look at the giant tangled mess of if elseif conditions on the first page and I noticed that were only checking for the most part either equal or not equal to 2 different colors. just simply counting the equals matches narrows things down considerably, then once its narrowed down by count then i just found unique differences between the various choices. This is obviously untested and i'm just dropping it here for shitz and giggles. I think I got it right but anyone should see what i'm stepping in here. expandcollapse popupdim $cols[3][5] $count=0 for $x=0 to 2 for $y=0 to 4 if $x = 1 and $y<>1 or $x<>1 or $x=1 and $y<>3 Then $cols[$x][$y]=_GetColor($hwnd,$0X[$x],$0Y[$y]) if $cols[$x][$y] = 0xFFFF00 or $cols[$x][$y]=0xFFB400 Then $count+=1 EndIf EndIf Next Next if $count=3 Then MsgBox(0, '', 'Number 1'); Number 1 ElseIf $count=6 Then MsgBox(0, '', 'Number 7'); Number 7 ElseIf $count=9 Then if $cols[0][0] = $cols[1][0] Then MsgBox(0, '', 'Number 3'); Number 3 ElseIf $cols[0][0] = $cols[2][0] Then MsgBox(0, '', 'Number 4'); Number 4 else MsgBox(0, '', 'Number 6'); Number 6 EndIf ElseIf $count = 10 Then if $cols[0][1]=0xFFFF00 and $cols[0][1]<>$cols[2][1] Then MsgBox(0, '', 'Number 5'); Number 5 ElseIf $cols[0][2]=0xFFFF00 and $cols[0][2]<>$cols[1][2] Then MsgBox(0, '', 'Number 0'); Number 0 ElseIf $cols[2][3]=0xFFFF00 and $cols[2][3]<>$cols[0][3] Then MsgBox(0, '', 'Number 9'); Number 9 ElseIf $cols[0][0]=0xFFFF00 and $cols[1][0]=$cols[0][0] then MsgBox(0, '', 'Number 2'); Number 2 Else MsgBox(0, '', 'Number 8'); Number 8 EndIf Else MsgBox(0, '', 'String S') EndIf Edited August 18, 2021 by markyrocks Spoiler "I Believe array math to be potentially fatal, I may be dying from array math poisoning" Link to comment Share on other sites More sharing options...
Loc Posted August 18, 2021 Author Share Posted August 18, 2021 I tried the code of 636C65616E. It gives error line 31 and markyrocks code gives error on this line :3 $cols[$x][$y]=_GetColor($hwnd,$0X[$x],$0Y[$y]) Link to comment Share on other sites More sharing options...
markyrocks Posted August 18, 2021 Share Posted August 18, 2021 (edited) ya no doubt the function doesn't exist in the script. It was meant to be just an idea how to clean up that ultra clustered section. I can't test it bc i don't have the pictures or whatever. Its meant to be more copy and pasted in rather than standing on its own. I didn't really bother to look into where the coordinates come from or whatever it would be up to you how to fit it in and modify as needed for actual usage. Edited August 18, 2021 by markyrocks Spoiler "I Believe array math to be potentially fatal, I may be dying from array math poisoning" Link to comment Share on other sites More sharing options...
636C65616E Posted August 18, 2021 Share Posted August 18, 2021 (edited) Yeah i copy-pasted, so there might be errors on the checks ... The failed assert comes from the fact I didnt called the _gdiplus_startup at start (and also shutdown at end), just add them. Edited August 19, 2021 by 636C65616E Loc 1 Link to comment Share on other sites More sharing options...
jugador Posted August 19, 2021 Share Posted August 19, 2021 @Nine and @636C65616E I never try Pixel Search or Ocr type code so what’s the advantage of FetchColors funcation over PixelGetColor Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now