1 (изменено: Malcev, 2023-05-13 21:18:43)

Тема: AHK: поиск картинки с IDXGIOutputDuplication + Gdip_FastImageSearch

Работает на win8+.
IDXGIOutputDuplication удобен тем, что делает снимок экрана только после обновления рабочего стола.
Gdip_FastImageSearch(), немного измененный мной (удалил некоторые бесполезные функции) отсюда:
https://github.com/MasterFocus/AutoHotk … Search.ahk
Cкрипт делает 10 снимков экрана и
1) ищет "needle.bmp" в координатах x1: = 0, y1: = 0, x2: = 1920, y2: = 1080.
2) ищет needle.png с прозрачным белым цветом и вариацией 10.
3) ищет все совпадения needle.bmp.
Также можно изменить направление поиска SearchDirection.
1 = left->right, top->bottom | 2 = left->right, bottom->top | 3 = right->left, bottom->top | 4 = right->left, top->bottom

GoSub, StartSection
oNeedles := [needle1 := {path:"needle.bmp"}, needle2 := {path: "needle.png", trans: 0xffffff}]
GoSub, createNeedle
return

f11::
loop 10
{
   tooltip % A_Index
   loop
   {
      VarSetCapacity(DXGI_OUTDUPL_FRAME_INFO, 48, 0)
      AcquireNextFrame := IDXGIOutputDuplication_AcquireNextFrame(Duplication, INFINITE := 0xFFFFFFFF, &DXGI_OUTDUPL_FRAME_INFO, desktop_resource)
      LastPresentTime := NumGet(DXGI_OUTDUPL_FRAME_INFO, 0, "int64")
      if (LastPresentTime != 0)
      {
         if (DesktopImageInSystemMemory = 1)
         {
            VarSetCapacity(DXGI_MAPPED_RECT, A_PtrSize*2, 0)
            IDXGIOutputDuplication_MapDesktopSurface(Duplication, &DXGI_MAPPED_RECT)
            haystack.Stride := NumGet(DXGI_MAPPED_RECT, 0, "int")
            haystack.Scan := NumGet(DXGI_MAPPED_RECT, A_PtrSize, "ptr")
         }
         else
         {
            tex := ID3D11Texture2D_Query(desktop_resource)
            ID3D11DeviceContext_CopyResource(d3d_context, staging_tex, tex)
            VarSetCapacity(D3D11_MAPPED_SUBRESOURCE, 8+A_PtrSize, 0)
            ID3D11DeviceContext_Map(d3d_context, staging_tex, 0, D3D11_MAP_READ := 1, 0, &D3D11_MAPPED_SUBRESOURCE)
            haystack.Scan := NumGet(D3D11_MAPPED_SUBRESOURCE, 0, "ptr")
            haystack.Stride := NumGet(D3D11_MAPPED_SUBRESOURCE, A_PtrSize, "uint")
         }

         if !Gdip_FastImageSearch(Haystack, needle1, 0, 0, 1920, 1080)
            msgbox % "needle1:`n" needle1.x " - " needle1.y

         if !Gdip_FastImageSearch(Haystack, needle2, 0, 0, 1920, 1080, variation := 10, SearchDirection := 2)
            msgbox % "needle2:`n" needle2.x " - " needle2.y

         list := Gdip_FastImageSearchList(Haystack, needle1, 0, 0, 1920, 1080)
         loop % list.count
            msgbox % "needleList:`n" list.x[A_Index] " - " list.y[A_Index]

         if (DesktopImageInSystemMemory = 1)
            IDXGIOutputDuplication_UnMapDesktopSurface(Duplication)
         else
         {
            ID3D11DeviceContext_Unmap(d3d_context, staging_tex, 0)
            ObjRelease(tex)
         }
         ObjRelease(desktop_resource)
         IDXGIOutputDuplication_ReleaseFrame(duplication)
         break
      }
      ObjRelease(desktop_resource)
      IDXGIOutputDuplication_ReleaseFrame(duplication)
   }
}
return



StartSection:
setbatchlines -1
IDXGIFactory := CreateDXGIFactory()
if !IDXGIFactory
{
   MsgBox, 16, Error, Create IDXGIFactory failed.
   ExitApp
}
loop
{
   IDXGIFactory_EnumAdapters(IDXGIFactory, A_Index-1, IDXGIAdapter)
   loop
   {
      hr := IDXGIAdapter_EnumOutputs(IDXGIAdapter, A_Index-1, IDXGIOutput)
      if (hr = "DXGI_ERROR_NOT_FOUND")
         break
      VarSetCapacity(DXGI_OUTPUT_DESC, 88+A_PtrSize, 0)
      IDXGIOutput_GetDesc(IDXGIOutput, &DXGI_OUTPUT_DESC)
      width := NumGet(DXGI_OUTPUT_DESC, 72, "int")
      height := NumGet(DXGI_OUTPUT_DESC, 76, "int")
      AttachedToDesktop := NumGet(DXGI_OUTPUT_DESC, 80, "int")
      if (AttachedToDesktop = 1)
         break 2
      ObjRelease(IDXGIOutput)  
   }
   ObjRelease(IDXGIAdapter)
}
if (AttachedToDesktop != 1)
{
   MsgBox, 16, Error, No adapter attached to desktop
   ExitApp
}
D3D11CreateDevice(IDXGIAdapter, D3D_DRIVER_TYPE_UNKNOWN := 0, 0, 0, 0, 0, D3D11_SDK_VERSION := 7, d3d_device, 0, d3d_context)
IDXGIOutput1 := IDXGIOutput1_Query(IDXGIOutput)
IDXGIOutput1_DuplicateOutput(IDXGIOutput1, d3d_device, Duplication)
VarSetCapacity(DXGI_OUTDUPL_DESC, 36, 0)
IDXGIOutputDuplication_GetDesc(Duplication, &DXGI_OUTDUPL_DESC)
DesktopImageInSystemMemory := NumGet(DXGI_OUTDUPL_DESC, 32, "uint")
sleep 50   ; As I understand - need some sleep for successful connecting to IDXGIOutputDuplication interface
VarSetCapacity(D3D11_TEXTURE2D_DESC, 44, 0)
NumPut(width, D3D11_TEXTURE2D_DESC, 0, "uint")   ; Width
NumPut(height, D3D11_TEXTURE2D_DESC, 4, "uint")   ; Height
NumPut(1, D3D11_TEXTURE2D_DESC, 8, "uint")   ; MipLevels
NumPut(1, D3D11_TEXTURE2D_DESC, 12, "uint")   ; ArraySize
NumPut(DXGI_FORMAT_B8G8R8A8_UNORM := 87, D3D11_TEXTURE2D_DESC, 16, "uint")   ; Format 
NumPut(1, D3D11_TEXTURE2D_DESC, 20, "uint")   ; SampleDescCount
NumPut(0, D3D11_TEXTURE2D_DESC, 24, "uint")   ; SampleDescQuality
NumPut(D3D11_USAGE_STAGING := 3, D3D11_TEXTURE2D_DESC, 28, "uint")   ; Usage
NumPut(0, D3D11_TEXTURE2D_DESC, 32, "uint")   ; BindFlags
NumPut(D3D11_CPU_ACCESS_READ := 0x20000, D3D11_TEXTURE2D_DESC, 36, "uint")   ; CPUAccessFlags
NumPut(0, D3D11_TEXTURE2D_DESC, 40, "uint")   ; MiscFlags
ID3D11Device_CreateTexture2D(d3d_device, &D3D11_TEXTURE2D_DESC, 0, staging_tex)
haystack := {}
DllCall("LoadLibrary", "str", "gdiplus")
VarSetCapacity(si, 8+2*A_PtrSize, 0)
NumPut(0x1, si, "uint")
DllCall("gdiplus\GdiplusStartup", "ptr*", pToken, "ptr", &si, "ptr", 0)
return

createNeedle:
for index, oNeedle in oNeedles
{
   DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", oNeedle.path, "ptr*", pBitmap%index%)
   DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap%index%, "uint*", width%index%)
   DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap%index%, "uint*", height%index%)
   VarSetCapacity(Rect%index%, 16, 0)
   NumPut(width%index%, Rect%index%, 8, "uint")
   NumPut(height%index%, Rect%index%, 12, "uint")
   VarSetCapacity(BitmapData%index%, 16+2*A_PtrSize, 0)
   DllCall("gdiplus\GdipBitmapLockBits", "ptr", pBitmap%index%, "ptr", &Rect%index%, "uint", ReadWrite := 3, "int", Format32bppArgb := 2498570, "ptr", &BitmapData%index%)
   Stride%index% := NumGet(BitmapData%index%, 8, "int")
   Scan%index% := NumGet(BitmapData%index%, 16, "ptr")
   if oNeedle.trans
   {
      if !MCode_SetBmpTrans
         MCode_SetBmpTrans := MCode("2,x86:VVdWU4PsBIt0JCyLTCQgi2wkGItEJByLfCQki1QkKMcGAAAAAIXJfmeFwH5jjUyFADH2MduNdgCJHCSNRDUA6w6NtCYAAAAAg8AEOcF0MQ+2WgI4WAJ18A+2WgE4WAF15w+2GjgYdeCLXCQsxkADAIPABIMDATnBddWNtgAAAACLHCQB+QH+g8MBOVwkIHWog8QEMcBbXl9dww==,x64:VlNIi1wkQMcDAAAAAInQSItUJDhFhcB+e4XAfndNY9lEjUj/SPfYSo1MiQRMjRSFAAAAAEUxyWYPH4QAAAAAAEmNBArrE2YuDx+EAAAAAABIg8AESDnIdC8PtnICQDhwAnXtD7ZyAUA4cAF14w+2MkA4MHXbxkADAEiDwASDAwFIOch11A8fAEGDwQFMAdlFOch1rDHAW17D")
      VarSetCapacity(TransColor, 4)
      NumPut(oNeedle.trans, TransColor, 0, "int")
      E := DllCall(MCode_SetBmpTrans, "ptr", Scan%index%, "int", width%index%, "int", height%index%, "int", Stride%index%, "ptr", &TransColor, "int*", MCount, "cdecl int")
   }
   oNeedle.width := width%index%, oNeedle.height := height%index%, oNeedle.pBitmap := pBitmap%index%, oNeedle.BitmapData := &BitmapData%index%, oNeedle.Scan := Scan%index%, oNeedle.Stride := Stride%index%
}
return

Gdip_FastImageSearchList(Haystack, needle, x1, y1, x2, y2, Variation=0, SearchDirection=1)
{
   OutputList := {}
   OutputList.count := 0
   While !Gdip_FastImageSearch(Haystack, needle, x1, y1, x2, y2, Variation, SearchDirection)
   {
      OutputList.count++
      y1 := needle.y+1
      OutputList.x[OutputList.count] := needle.x
      OutputList.y[OutputList.count] := needle.y
      innerX1 := needle.x+1
      innerY1 := needle.y
      innerY2 := innerY1+needle.height
      While !Gdip_FastImageSearch(Haystack, needle, innerX1, innerY1, x2, innerY2, Variation, SearchDirection)
      {
         OutputList.count++
         OutputList.x[OutputList.count] := needle.x
         OutputList.y[OutputList.count] := needle.y
         innerX1 := needle.x+1
      }
   }
   return OutputList
}

CreateDXGIFactory()
{
   if !DllCall("GetModuleHandle","str","DXGI")
      DllCall("LoadLibrary","str","DXGI")
   if !DllCall("GetModuleHandle","str","D3D11")
      DllCall("LoadLibrary","str","D3D11")
   GUID(riid, "{7b7166ec-21c7-44ae-b21a-c9ae321ae369}")
   hr := DllCall("DXGI\CreateDXGIFactory1", "ptr", &riid, "ptr*", ppFactory)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   return ppFactory
}

IDXGIFactory_EnumAdapters(this, Adapter, ByRef ppAdapter)
{
   hr := DllCall(NumGet(NumGet(this+0)+7*A_PtrSize), "ptr", this, "uint", Adapter, "ptr*", ppAdapter)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIAdapter_EnumOutputs(this, Output, ByRef ppOutput)
{
   hr := DllCall(NumGet(NumGet(this+0)+7*A_PtrSize), "ptr", this, "uint", Output, "ptr*", ppOutput)
   if hr or ErrorLevel
   {
      if (hr&=0xFFFFFFFF) = 0x887A0002   ; DXGI_ERROR_NOT_FOUND
         return "DXGI_ERROR_NOT_FOUND"
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   }
}

IDXGIAdapter_GetDesc(this, pDesc)
{
   hr := DllCall(NumGet(NumGet(this+0)+8*A_PtrSize), "ptr", this, "ptr", pDesc)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutput_GetDesc(this, pDesc)
{
   hr := DllCall(NumGet(NumGet(this+0)+7*A_PtrSize), "ptr", this, "ptr", pDesc)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutputDuplication_GetDesc(this, pDesc)
{
   DllCall(NumGet(NumGet(this+0)+7*A_PtrSize), "ptr", this, "ptr", pDesc)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutputDuplication_AcquireNextFrame(this, TimeoutInMilliseconds, pFrameInfo, ByRef ppDesktopResource)
{
   hr := DllCall(NumGet(NumGet(this+0)+8*A_PtrSize), "ptr", this, "uint", TimeoutInMilliseconds, "ptr", pFrameInfo, "ptr*", ppDesktopResource)
   if hr or ErrorLevel
   {
      if (hr&=0xFFFFFFFF) = 0x887A0027   ; DXGI_ERROR_WAIT_TIMEOUT
         return "DXGI_ERROR_WAIT_TIMEOUT"
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   }
}

D3D11CreateDevice(pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ByRef ppDevice, ByRef pFeatureLevel, ByRef ppImmediateContext)
{
   hr := DllCall("D3D11\D3D11CreateDevice", "ptr", pAdapter, "int", DriverType, "ptr", Software, "uint", Flags, "ptr", pFeatureLevels, "uint", FeatureLevels, "uint", SDKVersion, "ptr*", ppDevice, "ptr*", pFeatureLevel, "ptr*", ppImmediateContext)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

ID3D11Device_CreateTexture2D(this, pDesc, pInitialData, ByRef ppTexture2D)
{
   hr := DllCall(NumGet(NumGet(this+0)+5*A_PtrSize), "ptr", this, "ptr", pDesc, "ptr", pInitialData, "ptr*", ppTexture2D)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutputDuplication_MapDesktopSurface(this, pLockedRect)
{
   hr := DllCall(NumGet(NumGet(this+0)+12*A_PtrSize), "ptr", this, "ptr", pLockedRect)
   if hr or ErrorLevel
   {
      if (hr&=0xFFFFFFFF) = 0x887A0004   ; DXGI_ERROR_UNSUPPORTED
         return "DXGI_ERROR_UNSUPPORTED"
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   }
}

IDXGIOutputDuplication_UnMapDesktopSurface(this)
{
   hr := DllCall(NumGet(NumGet(this+0)+13*A_PtrSize), "ptr", this)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutputDuplication_ReleaseFrame(this)
{
   hr := DllCall(NumGet(NumGet(this+0)+14*A_PtrSize), "ptr", this)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutput1_DuplicateOutput(this, pDevice, ByRef ppOutputDuplication)
{
   hr := DllCall(NumGet(NumGet(this+0)+22*A_PtrSize), "ptr", this, "ptr", pDevice, "ptr*", ppOutputDuplication)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGIOutput1_Query(IDXGIOutput)
{ 
   hr := ComObjQuery(IDXGIOutput, "{00cddea8-939b-4b83-a340-a685226666cc}")
   if !hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   return hr
}

ID3D11Texture2D_Query(desktop_resource)
{ 
   hr := ComObjQuery(desktop_resource, "{6f15aaf2-d208-4e89-9ab4-489535d34f9c}")
   if !hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   return hr
}

ID3D11DeviceContext_CopyResource(this, pDstResource, pSrcResource)
{
   hr := DllCall(NumGet(NumGet(this+0)+47*A_PtrSize), "ptr", this, "ptr", pDstResource, "ptr", pSrcResource)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

ID3D11DeviceContext_CopySubresourceRegion(this, pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox)
{
   hr := DllCall(NumGet(NumGet(this+0)+46*A_PtrSize), "ptr", this, "ptr", pDstResource, "uint", DstSubresource, "uint", DstX, "uint", DstY, "uint", DstZ, "ptr", pSrcResource, "uint", SrcSubresource, "ptr", pSrcBox)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

ID3D11DeviceContext_Map(this, pResource, Subresource, MapType, MapFlags, pMappedResource)
{
   hr := DllCall(NumGet(NumGet(this+0)+14*A_PtrSize), "ptr", this, "ptr", pResource, "uint", Subresource, "uint", MapType, "uint", MapFlags, "ptr", pMappedResource)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

ID3D11DeviceContext_Unmap(this, pResource, Subresource)
{
   hr := DllCall(NumGet(NumGet(this+0)+15*A_PtrSize), "ptr", this, "ptr", pResource, "uint", Subresource)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

GUID(ByRef GUID, sGUID)
{
    VarSetCapacity(GUID, 16, 0)
    return DllCall("ole32\CLSIDFromString", "WStr", sGUID, "Ptr", &GUID) >= 0 ? &GUID : ""
}

_Error(val)
{
   msgbox % val
   ExitApp
}



;**********************************************************************************
;
; Gdip_FastImageSearch() - 08/MARCH/2013 21:00h BRT
; by MasterFocus, based on previous work by tic and Rseding91
; http://www.autohotkey.com/board/topic/71100-gdip-imagesearch/
;
; Licensed under CC BY-SA 3.0 -> http://creativecommons.org/licenses/by-sa/3.0/
; I waive compliance with the "Share Alike" condition exclusively for these users:
; - tic , Rseding91 , guest3456
;
;**********************************************************************************
;
;   Search direction:
;   0 = auto detect best direction [default]
;   1 = left->right, top->bottom ;; 2 = left->right, bottom->top
;   3 = right->left, bottom->top ;; 4 = right->left, top->bottom

Gdip_FastImageSearch(haystack, needle, x1, y1, x2, y2, Variation=0, SearchDirection=1)
{
   static MCode_ImageSearch1, MCode_ImageSearch2
   if !MCode_ImageSearch1
   {
      MCode_ImageSearch1 := MCode("2,x86:VVdWU4PsNItcJFiLRCR0OUQkbA+NaQEAAI1D/4ndiUQkCItEJFyNcP+LRCRsD69EJGCJdCQgiUQkHItEJGwB8A+vRCRgD690JGSJRCQki0QkaIl0JCgB2MHgAolEJCyLRCRoweACiUQkMItEJFSNBJiJRCQMi0QkcDlEJGgPjeAAAACLRCQwiUQkEItEJCyJRCQEi0QkaIkEJI22AAAAAIN8JHwBD4TdAQAAg3wkfAIPhHICAACDfCR8Aw+EHwEAAIN8JHwED4XtAAAAi0QkXIXAD47hAAAAi0QkBANEJFAx2zH/iUQkFIt0JByLTCQIhckPiKwAAACLRCQMi1QkFIlcJBgB2AHy6xqNtgAAAACAeP8AdTKD6QGD6ASD6gSD+f90fA+2Wv44WP514w+2Wv04WP112g+2Wvw4WPx014B4/wB00Y12AIMEJAGLBCSDRCQEBINEJBAEOUQkcA+FPf///4t8JGCDRCRsAQF8JByLRCRsAXwkJDlEJHQPhfP+//+LRCRIxwD/////i0QkTMcA/////7j/////6zGNdgCLXCQYg8cBA1wkZAN0JGA5fCRcD4Uz////i0QkSIs0JIt8JGyJMItEJEyJODHAg8Q0W15fXcONtCYAAAAAjXYAi3wkIIX/eNGLRCQEA0QkUIt0JCSLXCQoiUQkFItMJAiFyXhoi0QkDItUJBSJXCQYAdgB8usijbQmAAAAAI12AIB4/wAPhR7///+D6QGD6ASD6gSD+f90MA+2Wv44WP513w+2Wv04WP111g+2Wvw4WPx1zYPpAYPoBIPqBIP5/3XXjbQmAAAAAItcJBiD7wErXCRkK3QkYIP//3WA6Tj///+NtCYAAAAAi0wkXIXJD44l////i0QkEANEJFAx2zH/iUQkFIt0JByLTCQUi0QkVI0UMQHYMcmF7X5RiVwkGOsejbQmAAAAAIB4AwAPhXb+//+DwQGDwASDwgQ5zXQpD7ZaAjhYAnXgD7ZaAThYAXXXD7YaOBh10IPBAYPABIPCBDnNddqNdgCLXCQYg8cBA1wkZAN0JGA5fCRcdYvpl/7//422AAAAAItUJCCF0g+Ihf7//4tEJBADRCRQi3QkJItcJCiJRCQUi3wkIItMJBSLRCRUjRQxAdgxyYXtfk2JXCQY6xqNdgCAeAMAD4XW/f//g8EBg8AEg8IEOc10KQ+2WgI4WAJ14A+2WgE4WAF11w+2GjgYddCDwQGDwASDwgQ5zXXajXYAi1wkGIPvAStcJGQrdCRgg///dZDp+P3//w==,x64:QVdBVkFVQVRVV1ZTSIPsKIusJJgAAABEi5wk2AAAAIuEJMgAAABIiUwkcEyJxkyJy0iJVCR4OYQkuAAAAA+NfgEAAIuEJJAAAACNTf+JTCQURI1A/4uEJLgAAAAPr4QkoAAAAEKNPIUAAAAASGP/iUQkEIuEJLgAAAAByA+vhCSgAAAAD6+MJKgAAACJRCQYRInASY1EgQSJTCQcSIlEJAiLhCTAAAAAOYQksAAAAA+N4QAAAESLjCSwAAAARIuUJLAAAABFAcEPH4QAAAAAAEGD+wEPhBYCAABBg/sCD4TMAgAAQYP7Aw+EQgEAAEGD+wQPhQoBAACF7Q+OAgEAAEaNNI0AAAAARItsJBBFMeRFMf9NY/ZFhcAPiMYAAABJY8RJY9VEiWQkBESJwUgB+EwB8kgB2EgB8usfDx9EAACAeAMAdTqD6QFIg+gESIPqBIP5/w+EhgAAAEQPtmICRDhgAnXbRA+2YgFEOGABddBED7YiRDggdM2AeAMAdMeQQYPCAUGDwQFEOZQkwAAAAA+FOv///4uMJKAAAACDhCS4AAAAAQFMJBCLhCS4AAAAAUwkGDmEJMgAAAAPheD+//9Ii0QkcMcA/////0iLRCR4xwD/////uP/////rPGaQRItkJARBg8cBRAOkJKgAAABEA6wkoAAAAEQ5/Q+FFP///0iLRCRwi7wkuAAAAESJEEiLRCR4iTgxwEiDxChbXl9dQVxBXUFeQV/DDx9EAABEi3wkFEWF/3jIRo00jQAAAABEi2wkGESLZCQcTWP2RYXAeHFJY8RJY9VEiWQkBESJwUgB+EwB8kgB2EgB8usagHgDAA+FBv///4PpAUiD6ARIg+oEg/n/dDZED7ZiAkQ4YAJ120QPtmIBRDhgAXXQRA+2IkQ4IHXHg+kBSIPoBEiD6gSD+f910Q8fgAAAAABEi2QkBEGD7wFEK6QkqAAAAEQrrCSgAAAAQYP//w+FbP///+ka////Dx+EAAAAAACF7Q+OCv///0aNNJUAAAAARItsJBBFMeRFMf9NY/aLlCSQAAAAhdJ+bElj1UljzESJZCQETAHySI0EC0gDTCQISAHy6xoPHwCAeAMAD4U+/v//SIPABEiDwgRIOcF0MUQPtmICRDhgAnXeRA+2YgFEOGABddNED7YiRDggdcpIg8AESIPCBEg5wXXUDx9EAABEi2QkBEGDxwFEA6QkqAAAAEQDrCSgAAAARDn9D4Vs////6Vv+//9mDx+EAAAAAABEi3wkFEWF/w+IRP7//0aNNJUAAAAARItsJBhEi2QkHE1j9ouEJJAAAACFwH5vSWPVSWPMRIlkJARMAfJIjQQLSANMJAhIAfLrHWYPH0QAAIB4AwAPhXb9//9Ig8AESIPCBEg5wXQxRA+2YgJEOGACdd5ED7ZiAUQ4YAF100QPtiJEOCB1ykiDwARIg8IESDnBddQPH0QAAESLZCQEQYPvAUQrpCSoAAAARCusJKAAAABBg///D4Vo////6ZL9//8=")
      MCode_ImageSearch2 := MCode("2,x86:VVdWU4PsWIu8JJwAAACLhCSoAAAAi5wkmAAAADmcJJAAAAAPjY8CAACLjCSIAAAAi7QkpAAAAIn9i1wkeA+vyI0UtQAAAACLdCR4A4QkkAAAAA+vhCSEAAAAAdEBzgHQA0QkdIl0JEyNdAsDi4wkgAAAAIl0JDiLdCR8iUQkIIuEJJAAAACNXv+LtCSQAAAAiVwkGA+vtCSEAAAAjVn/AdgPr4QkhAAAAIlcJEAPr5wkiAAAAIl0JDyLdCR8iUQkRItEJHwDhCSMAAAAiVwkSMHgAolEJFSLhCSMAAAAweACiUQkUItEJHiNBLCJRCQwuAEAAAAp8IlEJCiLhCSUAAAAOYQkjAAAAA+NegEAAItEJEyLvCSMAAAAD7YAiUQkHItEJBgBxwOEJJQAAACJRCQsi0QkUIk8JIlEJASLRCRUiUQkCI12AItEJCgDBCSLfCQEi0wkHIlEJCSLRCQgD7YEOI0UKDnRfwYp6DnBfQ2LRCQ4gDgAD4XxAAAAg7wkoAAAAAEPhH0CAACDvCSgAAAAAg+ETwMAAIO8JKAAAAADD4SBAQAAg7wkoAAAAAQPhU8BAACLhCSAAAAAhcAPjkABAACLRCQ8x0QkFAAAAADHRCQMAAAAAIlEJBCLRCQIA0QkdIlEJDSLXCQYhdsPiOQAAACLRCQwi0wkNANEJBQDTCQQ6xyNtCYAAAAAjXYAg+sBg+gEg+kEg/v/D4S2AAAAD7ZR/g+2cP6NPCo5/n83Keo51nwxD7ZR/Q+2cP2NPCo5/n8iKeo51nwcD7ZR/A+2cPyNPCo5/n8NKeo51n2vjbQmAAAAAIB4/wB0ooMEJAGLBCSDRCQIBINEJAQEOUQkLA+Fv/7//4u8JIQAAACDhCSQAAAAAQF8JDyLhCSQAAAAAXwkIAF8JEQ5hCSYAAAAD4VD/v//i0QkbMcA/////4tEJHDHAP////+4/////+tKjXQmAJCDRCQMAYu8JIgAAACLnCSEAAAAAXwkFAFcJBCLRCQMOYQkgAAAAA+F5P7//4tEJGyLfCQkiTiLRCRwi7wkkAAAAIk4McCDxFhbXl9dw412AItEJECFwHjUi3wkRItcJEiJRCQMi0QkCANEJHSJfCQQiVwkFIlEJDSLXCQYhdsPiHwAAACLRCQwi0wkNANEJBQDTCQQ6xKD6wGD6ASD6QSD+/90XI10JgAPtlH+D7Zw/o08Kjn+fzEp6jnWfCsPtlH9D7Zw/Y08Kjn+fxwp6jnWfBYPtlH8D7Zw/I08Kjn+fwcp6jnWfa+QgHj/AA+FpP7//4PrAYPoBIPpBIP7/3Wog2wkDAGLRCQMi7QkiAAAAIucJIQAAAApdCQUKVwkEIP4/w+FUP///+n//v//jXYAi7wkgAAAAIX/D47t/v//i0QkPMdEJBQAAAAAx0QkDAAAAACJRCQQi0QkBANEJHSJRCQ0i3QkfItEJBQx24tMJDQDRCR4A0wkEIX2flyNdgAPtlECD7ZwAo08Kjn+fzEp6jnWfCsPtlEBD7ZwAY08Kjn+fxwp6jnWfBYPthEPtjCNPCo5/n8JKeo51n0NjXYAgHgDAA+FzP3//4PDAYPABIPBBDlcJHx1p4ucJIgAAACDRCQMAQFcJBSLRCQMi5wkhAAAAAFcJBA5hCSAAAAAD4Ve////6SL+//+NtgAAAACLTCRAhckPiBD+//+LRCREiUQkEItEJEiJRCQUi0QkQIlEJAyLRCQEA0QkdIlEJDSLVCR8i0QkFDHbi0wkNANEJHgDTCQQhdJ+X422AAAAAA+2UQIPtnACjTwqOf5/MSnqOdZ8Kw+2UQEPtnABjTwqOf5/HCnqOdZ8Fg+2EQ+2MI08Kjn+fwkp6jnWfQ2NdgCAeAMAD4Xs/P//g8MBg8AEg8EEOVwkfHWng2wkDAGLRCQMi5wkiAAAAIu0JIQAAAApXCQUKXQkEIP4/w+FX////+lG/f//,x64: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")
   }

   ;The dllcall parameters are the same for easier C code modification even though they arn't all used on the _ImageSearch1 version
   E := DllCall((Variation = 0 ? MCode_ImageSearch1 : MCode_ImageSearch2), "int*", x, "int*", y, "ptr", haystack.Scan, "ptr", needle.Scan, "int", needle.Width, "int", needle.Height, "int", haystack.Stride, "int", needle.Stride, "int", x1, "int", y1, "int", x2-needle.Width+1, "int", y2-needle.Height+1, "int", Variation, "int", SearchDirection, "int", 0, "int", 0, "cdecl int")
   needle.x := x, needle.y := y
   return (E = "") ? -13 : E
}

MCode(mcode)
{
  static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
  if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m))
    return
  if (!DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0))
    return
  p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
  if (c="x64")
    DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
  if (DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0))
    return p
  DllCall("GlobalFree", "ptr", p)
}

Чтобы проверить какой получается снимок с экрана нужно поставить следующий код перед Gdip_FastImageSearch().
Снимок сохранится в test.bmp.

DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count, "uint*", size)
VarSetCapacity(ci, size)
DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", &ci)
Loop % count
{
   EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "ptr"), "utf-16")
   if InStr(EncoderExtensions, "*.bmp")
   {
      pCodec := &ci + idx
      break
   }
}
DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", width, "int", height, "int", haystack.stride, "int", Format32bppArgb := 2498570, "ptr", haystack.scan, "ptr*", pBitmap)
DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", "test.bmp", "ptr", pCodec, "uint", 0)
DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
msgbox done
ExitApp

Для поиска использовал следующие c++ функции:

int Gdip_ImageSearch1(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, int v, int sd, int suX, int suY)
{
	int y1, y2, x1, x2, tx, ty;
	
	for (y1 = sy1; y1 < sy2; y1++)
	{
		for (x1 = sx1; x1 < sx2; x1++)
		{
			if (sd == 1){
				ty = y1;
				for (y2 = 0; y2 < nh; y2++)
				{
					tx = x1;
					for (x2 = 0; x2 < nw; x2++)
					{
						if (Needle[(4*x2)+(y2*Stride2)+2] == HayStack[(4*tx)+(ty*Stride1)+2]
						&& Needle[(4*x2)+(y2*Stride2)+1] == HayStack[(4*tx)+(ty*Stride1)+1]
						&& Needle[(4*x2)+(y2*Stride2)] == HayStack[(4*tx)+(ty*Stride1)]
						|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
							tx++;
						else
							goto NoMatch;
					}
					ty++;
				}
			} else if (sd == 2){
				ty = y1 + nh - 1;
				for (y2 = nh - 1; y2 > -1; y2--)
				{
					tx = x1;
					for (x2 = 0; x2 < nw; x2++)
					{
						if (Needle[(4*x2)+(y2*Stride2)+2] == HayStack[(4*tx)+(ty*Stride1)+2]
						&& Needle[(4*x2)+(y2*Stride2)+1] == HayStack[(4*tx)+(ty*Stride1)+1]
						&& Needle[(4*x2)+(y2*Stride2)] == HayStack[(4*tx)+(ty*Stride1)]
						|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
							tx++;
						else
							goto NoMatch;
					}
					ty--;
				}
			} else if (sd == 3){
				ty = y1 + nh - 1;
				for (y2 = nh - 1; y2 > -1; y2--)
				{
					tx = x1 + nw - 1;
					for (x2 = nw - 1; x2 > -1; x2--)
					{
						if (Needle[(4*x2)+(y2*Stride2)+2] == HayStack[(4*tx)+(ty*Stride1)+2]
						&& Needle[(4*x2)+(y2*Stride2)+1] == HayStack[(4*tx)+(ty*Stride1)+1]
						&& Needle[(4*x2)+(y2*Stride2)] == HayStack[(4*tx)+(ty*Stride1)]
						|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
							tx--;
						else
							goto NoMatch;
					}
					ty--;
				}
			} else if (sd == 4){
				ty = y1;
				for (y2 = 0; y2 < nh; y2++)
				{
					tx = x1 + nw - 1;
					for (x2 = nw - 1; x2 > -1; x2--)
					{
						if (Needle[(4*x2)+(y2*Stride2)+2] == HayStack[(4*tx)+(ty*Stride1)+2]
						&& Needle[(4*x2)+(y2*Stride2)+1] == HayStack[(4*tx)+(ty*Stride1)+1]
						&& Needle[(4*x2)+(y2*Stride2)] == HayStack[(4*tx)+(ty*Stride1)]
						|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
							tx--;
						else
							goto NoMatch;
					}
					ty++;
				}
			}
			
			Foundx[0] = x1; Foundy[0] = y1;
			return 0;
			NoMatch:;
		}
	}
	
	Foundx[0] = -1; Foundy[0] = -1;
	return -1;
}




int Gdip_ImageSearch2(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, int v, int sd, int suX, int suY)
{
	int y1, y2, x1, x2, tx, ty;
	
	for (y1 = sy1; y1 < sy2; y1++)
	{
		for (x1 = sx1; x1 < sx2; x1++)
		{
			if (Needle[(4*suX)+(suY*Stride2)] <= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]+v
			&& Needle[(4*suX)+(suY*Stride2)] >= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]-v
			|| Needle[(4*suX)+(suY*Stride2)+3] == 0)
			{
				if (sd == 1){
					ty = y1;
					for (y2 = 0; y2 < nh; y2++)
					{
						tx = x1;
						for (x2 = 0; x2 < nw; x2++)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx++;
							else
								goto NoMatch;
						}
						ty++;
					}
				} else if (sd == 2){
					ty = y1 + nh - 1;
					for (y2 = nh - 1; y2 > -1; y2--)
					{
						tx = x1;
						for (x2 = 0; x2 < nw; x2++)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx++;
							else
								goto NoMatch;
						}
						ty--;
					}
				} else if (sd == 3){
					ty = y1 + nh - 1;
					for (y2 = nh - 1; y2 > -1; y2--)
					{
						tx = x1 + nw - 1;
						for (x2 = nw - 1; x2 > -1; x2--)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx--;
							else
								goto NoMatch;
						}
						ty--;
					}
				} else if (sd == 4){
					ty = y1;
					for (y2 = 0; y2 < nh; y2++)
					{
						tx = x1 + nw - 1;
						for (x2 = nw - 1; x2 > -1; x2--)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx--;
							else
								goto NoMatch;
						}
						ty++;
					}
				}
			} else
				continue;
			
			Foundx[0] = x1; Foundy[0] = y1;
			return 0;
			NoMatch:;
		}
	}
	
	Foundx[0] = -1; Foundy[0] = -1;
	return -1;
}


int Gdip_SetBitmapTransColor(unsigned char * Scan, int Width, int Height, int Stride, unsigned char * Trans, int * MCount)
{
    int x1, y1, index;
    MCount[0] = 0;
    for (y1 = 0; y1 < Height; y1++) {
        for (x1 = 0; x1 < Width; x1++) {
            index = (4*x1)+(y1*Stride);
            if ( Scan[index+2] == Trans[2]
            &&   Scan[index+1] == Trans[1]
            &&   Scan[index+0] == Trans[0] ) {
                Scan[index+3] = 0;
                MCount[0]++;
            }
        }
    }
    return 0;
}

Тема для обсуждения