method 3 - GetObject - Loop and InBuf function (x32 only) (on binary data) [ranges from slow to super fast depending on the number of pixels found that we want to keep]
method 5 - GetObject - binary to hex string, RegExReplace 4 times [fast, best overall]
Included are some tests where all the pixels *were* and *were not* the desired font needle colour.
Note the machine code function, InBuf (x32 only) is used to search for pixels, RegExMatch cannot handle needle lengths or offsets that are an odd number of bytes. I talk about trying to create an x64 version of InBuf here, in case anyone can convert it.
InBuf function currently 32-bit only (machine code binary buffer searching) - AutoHotkey Community
Code: Select all
;[Gdip functions]
;GDI+ standard library 1.45 by tic - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=6517
;#Include Gdip_All.ahk
;WinMove, ahk_class Notepad,,,, 200, 200
;WinMove, ahk_class Notepad,,,, 100, 200
;Sleep 1000
;create a text string with 1s/0s based on a certain pixel colour
;method 1 - Gdip_GetPixel - Loop
;method 2 - GetObject - Loop and NumGet
;method 3 - GetObject - Loop and InBuf function (x32 only) (on binary data)
;method 4 - GetObject - binary to hex string, Loop and SubStr
;method 5 - GetObject - binary to hex string, RegExReplace 4 times
;q:: ;ARGB colour values to 1s/0s for use with OCR
;needle colour, check for pixels with this colour
vColRGB := 0x000000 ;black
;vColRGB := 0xabcdef
pToken := Gdip_Startup()
ControlGet, hCtl, Hwnd,, Edit1, A
WinGetPos, vWinX, vWinY, vWinW, vWinH, % "ahk_id " hCtl
vWinW := 500, vWinH := 500
vScreen := vWinX "|" vWinY "|" vWinW "|" vWinH
vScreen := 0 "|" 0 "|" A_ScreenWidth "|" A_ScreenHeight
pBitmap := Gdip_BitmapFromScreen(vScreen)
Gdip_GetImageDimensions(pBitmap, vImgW, vImgH)
if 0
{
;fill with one colour
pBrush := Gdip_BrushCreateSolid(0xff000000 | vColRGB)
;pBrush := Gdip_BrushCreateSolid(0xff000000 | ~vColRGB)
G := Gdip_GraphicsFromImage(pBitmap)
Gdip_FillRectangle(G, pBrush, 0, 0, vImgW, vImgH)
Gdip_DeleteBrush(pBrush)
Gdip_DeleteGraphics(G)
}
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
VarSetCapacity(DIBSECTION, vSizeDS:=A_PtrSize=8?104:84, 0)
DllCall("gdi32\GetObject", Ptr,hBitmap, Int,vSizeDS, Ptr,&DIBSECTION)
vAddr := NumGet(DIBSECTION, A_PtrSize=8?24:20, "Ptr") ;bmBits
vSize := NumGet(DIBSECTION, A_PtrSize=8?52:44, "UInt") ;biSizeImage
vHex := JEE_BinDataToHex(vAddr, vSize)
oTickStart := [], oTickEnd := [], oPixelMap := []
;MsgBox, % vSize " " (vImgW*4*vImgH)
;MsgBox, % ((vImgW+1)*vImgH)-1
;MsgBox, % vHex
;==================================================
;method 1 - Gdip_GetPixel - Loop
oTickStart.Push(A_TickCount)
vOutput := ""
VarSetCapacity(vOutput, (vImgW+1)*vImgH*2)
Loop, % vImgH
{
vY := A_Index-1
Loop, % vImgW
{
vX := A_Index-1
if (vColRGB = (Gdip_GetPixel(pBitmap, vX, vY) & 0xFFFFFF))
vOutput .= "1"
else
vOutput .= "0"
}
vOutput .= "`n"
}
vOutput := SubStr(vOutput, 1, -1)
oTickEnd.Push(A_TickCount)
oPixelMap.Push(vOutput)
;==================================================
;method 2 - GetObject - Loop and NumGet
oTickStart.Push(A_TickCount)
vOutput := ""
VarSetCapacity(vOutput, (vImgW+1)*vImgH*2)
vAddr2 := vAddr
Loop, % vImgH
{
Loop, % vImgW
{
if (vColRGB = (NumGet(vAddr2+0, "UInt") & 0xFFFFFF))
vOutput .= "1"
else
vOutput .= "0"
vAddr2 += 4
}
vOutput .= "`n"
}
vOutput := SubStr(vOutput, 1, -1)
vOutput := JEE_StrReverseLines(vOutput, "`n")
oTickEnd.Push(A_TickCount)
oPixelMap.Push(vOutput)
;==================================================
;method 3 - GetObject - Loop and InBuf function (x32 only) (on binary data)
oTickStart.Push(A_TickCount)
vOutput := ""
VarSetCapacity(vOutput, (vImgW+1)*vImgH*2)
Loop, % vImgH
vOutput .= Format("{:0" vImgW "}", 0) "`n"
vOutput := SubStr(vOutput, 1, -1)
VarSetCapacity(vDataNeedle, 3)
NumPut(vColRGB & 0xFF, &vDataNeedle, "UChar") ;(blue)
NumPut((vColRGB & 0xFF00) >> 8, &vDataNeedle+1, "UChar") ;(green)
NumPut((vColRGB & 0xFF0000) >> 16, &vDataNeedle+2, "UChar") ;(red)
vType := A_IsUnicode?"UShort":"UChar"
vPos := 0
Loop
{
vPos := InBuf(vAddr, &vDataNeedle, vSize, 3, vPos)
if (vPos = -1)
break
if !Mod(vPos, 4)
{
vOffset := vPos >> 2 ;divide by 4
vOffset := (vOffset + Floor(vOffset/vImgW)) * (!!A_IsUnicode+1)
;StrPut("1", &vOutput+vOffset, 1)
;StrPut("1", &vOutput+vOffset, 1, A_IsUnicode?"UTF-16":"CP0")
NumPut(49, &vOutput+vOffset, vType)
}
vPos++
}
vOutput := JEE_StrReverseLines(vOutput, "`n")
oTickEnd.Push(A_TickCount)
oPixelMap.Push(vOutput)
;==================================================
;method 4 - GetObject - binary to hex string, Loop and SubStr
oTickStart.Push(A_TickCount)
vOutput := ""
VarSetCapacity(vOutput, (vImgW+1)*vImgH*2)
vColBGR := Format("{:02X}{:02X}{:02X}", vColRGB & 0xFF, (vColRGB & 0xFF00) >> 8, (vColRGB & 0xFF0000) >> 16)
vPos := 1
Loop, % vImgH
{
Loop, % vImgW
{
if (vColBGR = SubStr(vHex, vPos, 6))
vOutput .= "1"
else
vOutput .= "0"
vPos += 8
}
vOutput .= "`n"
}
vOutput := SubStr(vOutput, 1, -1)
vOutput := JEE_StrReverseLines(vOutput, "`n")
oTickEnd.Push(A_TickCount)
oPixelMap.Push(vOutput)
;==================================================
;method 5 - GetObject - binary to hex string, RegExReplace 4 times
oTickStart.Push(A_TickCount)
;note: if hex is lowercase, hex needle must be lowercase, if we use RegEx in case-sensitive mode
vColBGR := Format("{:02x}{:02x}{:02x}", vColRGB & 0xFF, (vColRGB & 0xFF00) >> 8, (vColRGB & 0xFF0000) >> 16)
vOutput := RegExReplace(vHex, ".{8}", "_$0")
vOutput := RegExReplace(vOutput, "_" vColBGR "..", "1")
vOutput := RegExReplace(vOutput, "_.{8}", "0")
vOutput := RegExReplace(vOutput, ".{" vImgW "}", "$0`n")
vOutput := SubStr(vOutput, 1, -1)
vOutput := JEE_StrReverseLines(vOutput, "`n")
oTickEnd.Push(A_TickCount)
oPixelMap.Push(vOutput)
;==================================================
vOutput2 := "if the pixel maps match, then all the numbers below should equal 1:`r`n"
Loop, % oPixelMap.Length()
{
vIndex := A_Index
Loop, % oPixelMap.Length()
vOutput2 .= (oPixelMap[vIndex] = oPixelMap[A_Index])
vOutput2 .= "`r`n"
}
MsgBox, % vOutput2
;Loop, % oPixelMap.Length()
; MsgBox, % oPixelMap[A_Index]
vOutput3 := ((vImgW+1)*vImgH)-1 "`r`n"
Loop, % oPixelMap.Length()
vOutput3 .= (A_Index=1?"":" ") StrLen(oPixelMap[A_Index])
;MsgBox, % vOutput3
;vOutput1 := oPixelMap.1
;vOutput2 := oPixelMap.2
;JEE_WinMergeCompareStrings(vOutput1, vOutput2)
vOutput := StrReplace(vOutput, "`n", "`r`n")
vOutput := StrReplace(vOutput, "0", " ")
;vOutput := StrReplace(vOutput, "0", "..")
;vOutput := StrReplace(vOutput, "0", "_")
vOutput := StrReplace(vOutput, "1", "#")
;vOutput := StrReplace(vOutput, "1", Chr(9608)) ;FULL BLOCK
Clipboard := vOutput
MsgBox, % vOutput
;vOutput := StrReplace(vOutput, "0", " ")
;vOutput := StrReplace(vOutput, "1", Chr(9608)) ;FULL BLOCK
;Progress, zh0 b c0 fs1 w1000, % vOutput,,, Courier New
;Progress, zh0 b c0 fs1 w1000, % SubStr(vOutput, 1, 1000),,, Courier New
;Sleep 1000
;Progress, Off
;Notepad2 is useful for displaying the output
;Clipboard := vOutput
;MsgBox, % vOutput
vOutput4 := vImgW "x" vImgH "`r`n"
Loop, % oTickStart.Length()
vOutput4 .= A_Index "`t" (oTickEnd[A_Index] - oTickStart[A_Index]) "`r`n"
MsgBox, % Clipboard := vOutput4
DeleteObject(hBitmap)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
;MsgBox, % "done"
return
;==================================================
JEE_BinDataToHex(vAddr, vSize)
{
;CRYPT_STRING_HEX := 0x4 ;to return space/CRLF-separated text
;CRYPT_STRING_HEXRAW := 0xC ;to return raw hex (not supported by Windows XP)
DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Ptr,0, UIntP,vChars)
VarSetCapacity(vHex, vChars*2, 0)
DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Str,vHex, UIntP,vChars)
vHex := StrReplace(vHex, "`r`n")
vHex := StrReplace(vHex, " ")
return vHex
}
;==================================================
JEE_StrReverseLines(ByRef vText, vDelim:="`r`n")
{
if (vText = "")
return vText
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
oArray := StrSplit(vText, vDelim)
Loop, % oArray.Length() - 1
vOutput .= oArray[oArray.Length()+1-A_Index] vDelim
vOutput .= oArray.1
return vOutput
}
;==================================================
;Machine code binary buffer searching regardless of NULL - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/23627-machine-code-binary-buffer-searching-regardless-of-null/
InBuf(haystackAddr, needleAddr, haystackSize, needleSize, StartOffset=0)
{ Static fun
IfEqual,fun,
{
h=
( LTrim join
5589E583EC0C53515256579C8B5D1483FB000F8EC20000008B4D108B451829C129D9410F8E
B10000008B7D0801C78B750C31C0FCAC4B742A4B742D4B74364B74144B753F93AD93F2AE0F
858B000000391F75F4EB754EADF2AE757F3947FF75F7EB68F2AE7574EB628A26F2AE756C38
2775F8EB569366AD93F2AE755E66391F75F7EB474E43AD8975FC89DAC1EB02895DF483E203
8955F887DF87D187FB87CAF2AE75373947FF75F789FB89CA83C7038B75FC8B4DF485C97404
F3A775DE8B4DF885C97404F3A675D389DF4F89F82B45089D5F5E5A595BC9C2140031C0F7D0EBF0
)
VarSetCapacity(fun,StrLen(h)//2)
Loop % StrLen(h)//2
NumPut("0x" . SubStr(h,2*A_Index-1,2), fun, A_Index-1, "Char")
}
Return DllCall(&fun
, "uint",haystackAddr, "uint",needleAddr
, "uint",haystackSize, "uint",needleSize
, "uint",StartOffset)
}
;==================================================
;tests done using AHK U32 v1.1.26.01
;some results
;500x500
;1 2683
;2 343
;3 2216
;4 374
;5 125
;500x500
;1 2715
;2 343
;3 2246
;4 343
;5 110
;500x500
;1 3027
;2 343
;3 2215
;4 375
;5 78
;1280x720
;1 10031
;2 1248
;3 5991
;4 1248
;5 280
;1280x720
;1 11013
;2 1248
;3 5991
;4 1248
;5 296
;1280x720
;1 10078
;2 1279
;3 5990
;4 1248
;5 328
;some results: all pixels the needle colour
;1280x720
;1 10452
;2 936
;3 6490
;4 1154
;5 266
;1280x720
;1 10390
;2 936
;3 6489
;4 1186
;5 249
;1280x720
;1 10701
;2 905
;3 6646
;4 1123
;5 296
;some results: all pixels the needle colour (using StrPut instead of NumPut in method 3)
;1280x720
;1 10452
;2 936
;3 6708
;4 1123
;5 281
;1280x720
;1 10405
;2 998
;3 6833
;4 1186
;5 343
;1280x720
;1 10546
;2 905
;3 6833
;4 1123
;5 297
;some results: no pixels the needle colour
;1280x720
;1 10483
;2 936
;3 32
;4 904
;5 312
;1280x720
;1 10515
;2 998
;3 31
;4 967
;5 328
;1280x720
;1 10640
;2 936
;3 31
;4 967
;5 296
;==================================================