Jump to content

Is it possible to improve the search speed in this code?


Loc
 Share

Go to solution Solved by 636C65616E,

Recommended Posts

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 by Nine
Link to comment
Share on other sites

I knew i already tested that ...

func 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

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 by 636C65616E
Link to comment
Share on other sites

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 by Nine
Link to comment
Share on other sites

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
; 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 by 636C65616E
Link to comment
Share on other sites

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

#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

  • Solution

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.

#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 by 636C65616E
Link to comment
Share on other sites

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

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 by Loc
Link to comment
Share on other sites

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

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)

#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 by 636C65616E
Link to comment
Share on other sites

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.

dim $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 by markyrocks
Link to comment
Share on other sites

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 by markyrocks
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...