免输密码全自动登录有道云笔记Windows客户端 2024年8月22日

 

   免输密码全自动登录有道云笔记Windows客户端 2024年8月22日  

免输密码全自动登录有道云笔记Windows客户端 2024年8月22日

因为有道云笔记可以批量全文搜索笔记文档的正文内容,所以我选择使用有道云笔记而  不使用  腾讯文档和金山文档。

;
; 免输入密码全自动登录有道云笔记Windows客户端 2024年8月22日
;
; 因为有道云笔记可以批量全文搜索笔记文档的正文内容,所以我选择使用有道云笔记而不使用腾讯文档和金山文档。
;
; 飞书也可以批量全文搜索文档文件的正文内容,但是为了集中统一管理和备份数据只能用一个笔记软件。
; 飞书的应用宝下载量没有有道云笔记多
; 指纹加密U盘&FindText-v9.7-by-FeiYue&Loop-if-break&有道云笔记&Index-Your-Files&mstsc&零层壹号&WinSCP&USMv5&Acronis-true-Image-2021-WinPE&GoodSync&向日葵控控A2
;
; Index-Your-Files 能够索引DVD-ROM只读光盘虚拟光驱量产U盘,MasterSeeker、Everything、Quick Search、UltraSearch和Listary无法索引DVD-ROM只读光盘虚拟光驱量产U盘并自定义指定索引数据库文件存放的位置。
;
; 常见的指纹加密U盘有 联想FU100、联想FU200、移速双接口御存系列、
; FP651星空黑 梵想指纹加密固态U盘双接口锌合金外壳512GB、亿捷指纹加密U盘、毕亚兹指纹加密U盘、
; 三星T7  Touch指纹加密移动固态硬盘2TB、联想ThinkBook指纹加密移动固态硬盘2TB、FM6忆捷指纹加密固态移动硬盘2TB容量等。

; 1、设置此脚本的运行参数
#SingleInstance force
#Persistent
#WinActivateForce
DetectHiddenWindows,On
SetTitleMatchMode,2
#Include FindText.ahk
t1:=A_TickCount, Text:=X:=Y:=""

; 输入两个空格,身份验证通过后再登录有道云笔记
; 请使用飞跃的  AHK 源码加密器 v3.1 -FeiYue 加密工具对AutoHotkey源代码加密保护SFTP登录信息
; 请使用专用函数对密码字符串进行加密处理
InputBox, password, Enter Password, (your Input will be hidden), hide
NeedYDPWD:="  "
IfEqual,password,%NeedYDPWD%
{

	; 2、关闭并重新运行有道云笔记Windows客户端
	WinClose,ynote-desktop ahk_exe 有道云笔记.exe
	Process,Close,有道云笔记.exe
	Run,"C:\Program Files\ynote-desktop\有道云笔记.exe"

	; 3、免输入密码登录有道云笔记
	SetTimer,LoginYD,100
}
else
{
	ExitApp
}
return

LoginYD()
{
	WinActivate,ynote-desktop ahk_exe 有道云笔记.exe
#IfWinActive,ynote-desktop ahk_exe 有道云笔记.exe

	; 不选"自动登录"复选框,注意:需要先选中复选框然后再用FindText()截屏抓图灰度阈值二值化。
	; 不同的选取范围多截屏抓图几次,多截图,同时一起找多个图,防止找不到图脚本失效
	Text:="|<>*222$103.000000000000000007zzw00000000000007zzz00000000000003zzzU07000M01n0001zzzk0301zA3zP0zzUzzzs0zzs0603jA00kTzzw0M0A0DylXA00MDzxy0A0601XBUw7zw7zwT060300lXzw0063zwTU3zzbzMnU3U031zwTk1U0kMAPU0TzzwzQTs0k0MM6Dzzk0M0T0Tw0M0ABX6A0MQAADkTy0A064lX60A37C7wDz07zz6BVX060nw3zDzU301aSklzz07w1zzzk1U0ntsMA7USr0zzzs0k0M0MQ770sMkTzzw0Tzw0Nw1X0kA7Dzzy0A060A0Tzz1w07zzz00000000000004"

	if (ok:=FindText(X, Y, 747-150000, 587-150000, 747+150000, 587+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}


	Text:="|<>*197$61.00000000003zzzw000003zzzz000003zzzzk00001zzzzs00000zzzzw000k0Tzzzy000M0Dzzzz000M07zzzzU0Dzznzzzzk07zztzzyTs0300Azzy7w01U06Tzy3y00k03Dzy3z00M01byS3zU0Dzzny63zk07zztz03zs0300Azk3zw01U06Tw3zy00k03Dy1zz00TzzbzVzzU0Dzznztzzk0600Nzzzzs0300Azzzzw01U06Tzzzy00k03Dzzzz00TzzbzzzzU0DzzlzzzzU0600MTzzzU000000000000000U"

	if (ok:=FindText(X, Y, 1119-150000, 893-150000, 1119+150000, 893+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}


	Text:="|<>*197$87.000000000000000zzzz0000000000Dzzzw0000000003zzzzk000000000Tzzzy0000000003zzzzk003000060Tzzzy000M0000k3zzzzk00600Ty60Tzzzy00zzz1zkk3zzzzk07zzs0060Tzzby00k0300DznzzsTk0600M01zyTzy3y00k03000knzzUzk0600M0066TtsDy00zzz7ztkny63zk07zzs3UA6Tk0zy00k030M1Unz0Dzk0600M30A6Tw3zy00k030l1UnzUTzk07zzs6QA6Ty7zy00zzz1VXUnztzzk0600MAAM6Tzzzy00k0331n0nzzzzk0600MTykCTzzzy00k033wy1Xzzzzk07zzs01WQTzzzy00zzz00QTVzzzzU0600M031k7zzzs000000000000000000000000U"

	if (ok:=FindText(X, Y, 1132-150000, 893-150000, 1132+150000, 893+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}


	Text:="|<>*198$111.0000000000000000000zzzz00000000000000Dzzzw00000000000003zzzzk0000000000000Tzzzy00000000000003zzzzk00300006000EETzzzy000M0000k00673zzzzk00600Ty60DyPkTzzzy00zzz1zkk01ns3zzzzk07zzs00608AACTzzby00k0300DznXVrXzzsTk0600M01zyCs7kTzy3y00k03000kkzzw3zzUzk0600M00663jxsTtsDy00zzz7ztklk07ny63zk07zzs3UA6w00DTk0zy00k030M1UnTzyHz0Dzk0600M30A63U1kTw3zy00k030l1UkM063zUTzk07zzs6QA6300kTy7zy00zzz1VXUkM063ztzzk0600MAAM63zzkTzzzy00k0331n0kM0K3zzzzk0600MTykC0s70Tzzzy00k033wy1U3Vk3zzzzk07zzs01WQ0AA0Tzzzy00zzz00QTbzzztzzzzU0600M031k00007zzzs00000000000000000000000000000000U"

	if (ok:=FindText(X, Y, 1144-150000, 893-150000, 1144+150000, 893+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}


	; 等待出现并点击邮箱账号文本输入框

	Text:="|<>*222$110.3U1k00M00070001k1k0k0y00D0001k000M0M0A0TU01s000Q1zsDzjz70CQ00C00070Ty7zvzzzbXU01k001k73Vq1m3zvkS00C00Dztkktktk61zzk03U03zyQQwCAC3Vzzz00w00lnb66N11UqQztU0D00AQtnU61zzBW00007s037CQs1UzznMTkA01y00lnbA0QC0BqDwn00tk0AQtr1zvU3NXXAk0CQ037CQsTys0rzsnA03XU0zzb70MDzxzyAn01ks0DztlkC3zz1XzAk0w7037CQC3ss0kMznA0C1k0lnb3VzC0A6CAn070C0AQtkMSvU31jXAk3k1k37CQ6DazzrzznA0s0S0lnb3bMDzzzzwn0Q03kAQtzta3U3NXX4kS00S3zyTwNUs0kMskAD003kzzby4MC0A6CA37U00TA0tk063zz1XXDlk003X0CQ01UzzkMznw8000E00700MC0A6CsS00000001k06103U"

	if (ok:=FindText(X, Y, 1195-150000, 565-150000, 1195+150000, 565+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		Sleep,50
		; 这里需要替换成你的邮箱地址和有道云笔记登录密码
		; 先分别用5种完全不同的方法切换输入法到英文小写状态
		DllCall("SendMessage" , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint,WinExist("A")), UInt, 0x0283   ,  Int, 0x002  ,  Int, 0x00)   ;切换搜狗拼音成英文半角状态
		Sleep,100
		SendPlay,595076941@qq.com
		Sleep,10
		Send,{tab}
		Sleep,10
		Send,ba?.l?34794
		loop,10
		{

			; 等待出现并点击登录按钮

			Text:="|<>*222$63.zzvxzzzzzzy06SDzz000TU0l3zzs003zz71zzzzzyTztsTDzzzznwyDbVzzzzyTnXyEzzs003z8zsDzz000Tw807zzzzznzX04DzzzzyTkzzsTzzzznwDzzUzs00016003Dz0000DU00Tzzztzzwzznzzlz7wzbzyTzz7sy7wzznzzwD3XzbzyTzzts0zw003zzzx8TzU00Tzzw9XzwjzHzzw3C7zwzlzzw3tsDzlwTzz3zDkDzTbzztztzV00007zzUDzM0000zzw3zzU"

			if (ok:=FindText(X, Y, 1440-150000, 800-150000, 1440+150000, 800+150000, 0, 0, Text))
			{
				FindText().Click(X, Y, "L")
				SetTimer,LoginYD,Delete
				ExitApp
			}
			; 缩小截图抓图图片大小,再找一遍
			Text:="|<>*190$36.DbzzzzC3zz0047zz00UTzzzzVszzzzlkzzzzk1zzU0s7zzU00Dzzzz27zzzzz0zzzzzkTw0000Tw0007zzjwz7zz7wz7zz1wz7zzkwz7zzsw07zzzU07zzw0w7zzU4szzw0wlzzs7wnzzwzs00Tzw000Tzy0U"

			if (ok:=FindText(X, Y, 1441-150000, 800-150000, 1441+150000, 800+150000, 0, 0, Text))
			{
				FindText().Click(X, Y, "L")
				SetTimer,LoginYD,Delete
				ExitApp
			}
		}
	}

	; 不同的截图范围大小,再找一遍

	Text:="|<>*221$54.0000000000A0003U000S0003U000D0003UDz070003UDz03U003UC701k01zzC601k01zzCC01s01XbCA01s01XbCQ03w01XbCQ03w01XbCM07C01XbCs07C01XbCQ06701zzCC0C701zzCC0S3U1XbC70Q3U1XbC70s1k1XbC31s0s1XbC31k0w1XbC73U0S1XbDzD00D1zzDyS007VzzDww003tU7C0s001lU7C0E000U00C00000000C0U"

	if (ok:=FindText(X, Y, 1196-150000, 565-150000, 1196+150000, 565+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		Sleep,50
		; 这里需要替换成你的邮箱地址和有道云笔记登录密码
		; 先分别用5种完全不同的方法切换输入法到英文小写状态
		DllCall("SendMessage" , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint,WinExist("A")), UInt, 0x0283   ,  Int, 0x002  ,  Int, 0x00)   ;切换搜狗拼音成英文半角状态
		Sleep,100
		SendPlay,595076941@qq.com
		Sleep,10
		Send,{tab}
		Sleep,10
		Send,ba?.l?34794
		loop,10
		{

			; 等待出现并点击登录按钮

			Text:="|<>*222$63.zzvxzzzzzzy06SDzz000TU0l3zzs003zz71zzzzzyTztsTDzzzznwyDbVzzzzyTnXyEzzs003z8zsDzz000Tw807zzzzznzX04DzzzzyTkzzsTzzzznwDzzUzs00016003Dz0000DU00Tzzztzzwzznzzlz7wzbzyTzz7sy7wzznzzwD3XzbzyTzzts0zw003zzzx8TzU00Tzzw9XzwjzHzzw3C7zwzlzzw3tsDzlwTzz3zDkDzTbzztztzV00007zzUDzM0000zzw3zzU"

			if (ok:=FindText(X, Y, 1440-150000, 800-150000, 1440+150000, 800+150000, 0, 0, Text))
			{
				FindText().Click(X, Y, "L")
				SetTimer,LoginYD,Delete
				ExitApp
			}

			; 缩小截图抓图图片大小,再找一遍
			Text:="|<>*190$36.DbzzzzC3zz0047zz00UTzzzzVszzzzlkzzzzk1zzU0s7zzU00Dzzzz27zzzzz0zzzzzkTw0000Tw0007zzjwz7zz7wz7zz1wz7zzkwz7zzsw07zzzU07zzw0w7zzU4szzw0wlzzs7wnzzwzs00Tzw000Tzy0U"

			if (ok:=FindText(X, Y, 1441-150000, 800-150000, 1441+150000, 800+150000, 0, 0, Text))
			{
				FindText().Click(X, Y, "L")
				SetTimer,LoginYD,Delete
				ExitApp
			}




		}
	}

#IfWinActive

	; 如果能够找到屏幕左上角的同步按钮图片表示已经登录成功,直接退出脚本,此处多找几个图片,只要找到其中1个图片就表示已经登录成功

	Text:="|<>*222$34.00000001zU000TzU007zzU00y0T007U0S00w00w07U01s0Q003U1U00700000Q00000s60003Uw000C7s000s70007sQ000D1k000M700000C00000s001U1k00C07U01s0D00D00S01s00y0T001zzs001zy0001zU0000000U"

	if (ok:=FindText(X, Y, 280-150000, 59-150000, 280+150000, 59+150000, 0, 0, Text))
	{
		; FindText().Click(X, Y, "L")
		SetTimer,LoginYD,Delete
		ExitApp
	}


	Text:="|<>*222$30.000007zzzsDzzzwTzzzyQC00CQC00CQC00CQC00CQC0ECQC0sCQC1sCQC1kCQC3UCQC7UCQCD0CQCD0CQC7UCQC3UCQC1kCQC1sCQC0sCQC0ECQC00CQC00CQC00CQC00CTzzzyDzzzw7zzzs00000U"

	if (ok:=FindText(X, Y, 324-150000, 60-150000, 324+150000, 60+150000, 0, 0, Text))
	{
		; FindText().Click(X, Y, "L")
		SetTimer,LoginYD,Delete
		ExitApp
	}


}



; 注意请使用FindText的截屏抓图功能截图,修剪二值_化图片小一点,多截屏几次,多抓图几次,同时找多个二值化图片,防止找不到图片
; 操作需要等待窗口或者控件的出现就用LOOP循环,IF判断和SLEEP等待
; 操作有先后顺序关系就用大括号嵌套各层操作
; 不同电脑,不同显卡,不同屏幕分辨率下显示的图片不同就在不同电脑上多抓几次图,同时并行查找搜索FindText多个图片
; 需要并行同时多个操作时请使用SetTimer语句,以指定的时间间隔自动并重复运行子程序.
; 需要并行同时多个操作时不要用LOOP循环,loop循环无法并行执行多个操作

 

免输密码全自动登录有道云笔记Windows客户端 2024年8月22日

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

免输密码全自动登录有道云笔记Windows客户端 2024年8月22日

 

; 免输密码全自动登录金山文档Windows客户端 2024年8月22日
;
; 指纹加密U盘&FindText-v9.7-by-FeiYue&Loop-if-break&金山文档&Index-Your-Files&mstsc&零层壹号&WinSCP&USMv5&Acronis-true-Image-2021-WinPE&GoodSync&向日葵控控A2
;
; Index-Your-Files 能够索引DVD-ROM只读光盘虚拟光驱量产U盘,MasterSeeker、Everything、Quick Search、UltraSearch和Listary无法索引DVD-ROM只读光盘虚拟光驱量产U盘并自定义指定索引数据库文件存放的位置。
;
; 常见的指纹加密U盘有 “FP651星空黑 梵想指纹加密固态U盘双接口锌合金外壳512GB”  联想FU100、联想FU200、移速双接口御存系列、亿捷指纹加密U盘、毕亚兹指纹加密U盘等。

; 设置此脚本的运行参数
#SingleInstance force
#Persistent
#WinActivateForce
DetectHiddenWindows,On
SetTitleMatchMode,2
#Include FindText.ahk
t1:=A_TickCount, Text:=X:=Y:=""

; 关闭并重新运行金山文档
WinClose,登录 ahk_exe 金山文档.exe
Process,Close,金山文档.exe
Run,"C:\Program Files (x86)\金山文档\金山文档.exe"

SetTimer,LoginJS,500
return

LoginJS()
{
	WinActivate,登录 ahk_exe 金山文档.exe
#IfWinActive,登录 ahk_exe 金山文档.exe
	; 等待出现并点击账号密码的锁形按钮,
	; 注意请使用FindText的截屏抓图功能截图,灰度阈值我填222,可以先灰度阈值二值化后再修剪二值化图片
	Text:="|<>*222$24.000003k00Dw00yT00s701k3U1U1U3U1k300k300k7zzsDzzwQ00CM006M006M006M006M006M1k6M3k6M3U6M006M006M006M006M006Q00CDzzw7zzs0000U"
	if (ok:=FindText(X, Y, 1850-150000, 1238-150000, 1850+150000, 1238+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}

	; 等待出现并点击同意按钮
	Text:="|<>*222$41.zzzzzzzzzzzwzz003zxzyzzrU01xzzjvzDvzzTtwzo02s007jzxzzzzTzvs01ys1rrzvxrvjU07vjrTTzjrTiy00TizRxxyzQ0vqNyyvzrAtgxzziNzQvzzRs1zrzUzzzzzzzzzzy"
	if (ok:=FindText(X, Y, 1762-150000, 1014-150000, 1762+150000, 1014+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
	}

	; 等待出现并点击手机号输入框,由于此处操作有先后顺序关系,所以嵌套大括号保证各操作的先后顺序正确
	; 操作需要等待窗口或者控件的出现就用LOOP循环,IF判断和SLEEP等待
	; 操作有先后顺序关系就用大括号嵌套各层操作
	; 不同电脑,不同显卡,不同屏幕分辨率下显示的图片不同就在不同电脑上多抓几次图,同时并行查找搜索FindText多个图片
	; 需要并行同时多个操作时请使用SetTimer语句,以指定的时间间隔自动并重复运行子程序.
	; 需要并行同时多个操作时不要用LOOP循环,loop循环无法并行执行多个操作

	Text:="|<>*222$43.00000000k000000M00A1U0A7y60k06327zzszxX6Em0NalaAFkAnNXP0M6NgkADz3AqE661VaPQTv0kzxb1VzsNalUkkAAnMMyM66NgATg33Aq6STzVaP3/30knBVhVUMTyzYkkAA3M0MTy00A0AA3006000000000001"
	if (ok:=FindText(X, Y, 2067-150000, 550-150000, 2067+150000, 550+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		Sleep,500
		Send,138####5488
		Sleep,100
		Send,{tab}
		Sleep,100
		Send,ba????3

		; 等待出现并点击智能验证的跳动圆点
		Text1:="|<>*222$51.000000000000zs000000zzs00000zzzs0000Tzzzk0007zzzz0003zzzzy000zzzzzs00DzzzzzU03zzzzzy00zzzzzzs07zzzzzz01zzzzzzw0Tzzzzzzk3zzzzzzy0zzzsTzzs7zzw0zzz1zzk00DzwDzy001zzVzzk00DzwDzy001zzXzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyDzz001zzVzzs00TzwDzzU07zzVzzw00zzw7zzk0Dzz0zzzU7zzs3zzz3zzy0Tzzzzzzk1zzzzzzw07zzzzzz00zzzzzzs03zzzzzy00DzzzzzU00zzzzzs003zzzzy0007zzzz0000Tzzzk0000zzzs00001zzw000000zs000000000004"
		Text2:="|<>*222$41.000000000zzs0007zzw000Tzzw003zzzy00Dzzzy00zzzzy03zzzzy0Dzzzzy0TzwTzw1zz03zw7zk00zwDzU01zszz003ztzy007znzw00Dzbzs00TzDzk00zyTzU01zwzz003ztzy007znzw00Dzbzw00zzDzs01zyTzs07zwzzs0Tztzzs1zznzzsDzzXzzzzzy7zzzzzw7zzzzzk7zzzzz0Dzzzzy0Dzzzzs0DzzzzU0Dzzzy00Dzzzs007zzz0007zzw0003zzU00000000E"
		Text3:="|<>*222$41.000000000zzs0007zzw000Tzzw003zzzy00Dzzzy00zzzzy03zzzzy0Dzzzzy0TzwTzw1zz03zw7zk00zwDzU01zszz003ztzy007znzw00Dzbzs00TzDzk00zyTzU01zwzz003ztzy007znzw00Dzbzw00zzDzs01zyTzs07zwzzs0Tztzzs1zznzzsDzzXzzzzzy7zzzzzw7zzzzzk7zzzzz0Dzzzzy0Dzzzzs0DzzzzU0Dzzzy00Dzzzs007zzz0007zzw0003zzU00000000E"
		if ((ok:=FindText(X, Y, 1350-150000, 477-150000, 1350+150000, 477+150000, 0, 0, Text1)) or (ok:=FindText(X, Y, 1350-150000, 477-150000, 1350+150000, 477+150000, 0, 0, Text2))  or (ok:=FindText(X, Y, 1350-150000, 477-150000, 1350+150000, 477+150000, 0, 0, Text3)))
		{
			FindText().Click(X, Y, "L")
			; 等待验证成功
			Sleep,3000

			; 等待出现并点击立即登录按钮
			Text:="|<>*222$86.zzzzzzzzzzzzzzzzTzzzzzzzzzzzzznzzzzzzwxzzzzzyTz0A0s1AzU03zzjzrvDDysTzzwz000RynnjCTTzzDzzzzTgwlbnDU03zjzjk3DD3y7zzwztznxynnt0HzzzDyTwzTgwwzzC000TnzDrvDAzzwzxzzwzbw0nnM03yzTrzDtzTgwyzyzrntztyzrzDDjzjyQwzyTDxnnnvzvzr6TzbnzTQwy00zzIDzwtzrXDDXyjzZbzzSTxknryTbzbQzzzDz1o1zrnzbrnw000VxDzwxznxz7zzzxznz000/kTzzzzzzzzzzzzzzzs"
			loop,100
			{
				if (ok:=FindText(X, Y, 2188-150000, 943-150000, 2188+150000, 943+150000, 0, 0, Text))
				{
					FindText().Click(X, Y, "L")
					; 登录成功,关闭此脚本
					SetTimer,LoginJS,Delete
					ExitApp
					break
				}
				else
				{
					Sleep,100
				}
			}
		}
		else
		{
			Sleep,100
		}
	}
#IfWinActive

	; 如果找到"我的云文档"字样表示已经登录成功,直接退出脚本
	; 操作需要等待窗口或者控件的出现就用LOOP循环,IF判断和SLEEP等待
	; 操作有先后顺序关系就用大括号嵌套各层操作
	; 不同电脑,不同显卡,不同屏幕分辨率下显示的图片不同就在不同电脑上多抓几次图,同时并行查找搜索FindText多个图片
	; 需要并行同时多个操作时请使用SetTimer语句,以指定的时间间隔自动并重复运行子程序.
	; 需要并行同时多个操作时不要用LOOP循环,loop循环无法并行执行多个操作

	Text5:="|<>*222$107.00000000000000000003k0w7000000s0D0w0Tzi1wD0Tzz03s0SNvXzzy3sy0zzy07k0zvrbzzyTxw1zzwzzztvzzDwzxzvznzztzzzzzzw3txnzzzU003zzzzzzszzzzzzz0007zzzzrzVzzzzjwy0001w7lzzznzzzzTtzzzzVwT1xzzUyTAyxnzzzz3sy3zzz1xyxzzrzzzy3vsDy0yDzzvzzzUTA07zkTw1wzzzrzjz1zw07z0zzztzzzDjDy7vw0Dy1zzznz7zTSTwTXw0Ds3xzzUyTzywHty3s1zw7s0T1zzTzs7rzzsTzz3k0y3zzzzrzDzzvzrzbjzwzzzzzjyTzzzz7zDTzszwzjjDwTzzrs3wSzzly0zTSTk006D01kw0DU00000000000000000E"
	Text6:="|<>*222$107.000000000000Q0000001k0M7000000M060M01vg1kA0Tzz00s0AklXy7wDwzs000TzzwNlXUQCQMNUk0003060lnC0sQQkq1U00070QDxqs1ks1Vw30000C0s31hUzzzv3k60000A1U60M073U66MA0000Q70QTzUC76AAsPzzzUMA0y030Q6CTssk3k00ss1y060zgskklU7000nU7i0A7sTVVVn0QA00y0DA0MvUy331a0sw01s0qDzk71kq60A3Us03s1g01UC7lgA0sC0s0Cw0M030QtbMM1kM0s1kS0k060z3gkk3Vk1sS0D1U0ADU1tzU67zzvU03XDzs000337w000k000600k000000000000000004"
	if ((ok:=FindText(X, Y, 159-150000, 529-150000, 159+150000, 529+150000, 0, 0, Text5)) or (ok:=FindText(X, Y, 159-150000, 529-150000, 159+150000, 529+150000, 0, 0, Text6)))
	{
		SetTimer,LoginJS,Delete
		ExitApp
	}
}

 

 

; 免输密码全自动登录金山文档Windows客户端 2024年8月22日
;
; 指纹加密U盘&FindText-v9.7-by-FeiYue&Loop-if-break&金山文档&Index-Your-Files&mstsc&零层壹号&WinSCP&USMv5&Acronis-true-Image-2021-WinPE&GoodSync&向日葵控控A2
;
; Index-Your-Files 能够索引DVD-ROM只读光盘虚拟光驱量产U盘,MasterSeeker、Everything、Quick Search、UltraSearch和Listary无法索引DVD-ROM只读光盘虚拟光驱量产U盘并自定义指定索引数据库文件存放的位置。
;
; 常见的指纹加密U盘有 “FP651星空黑 梵想指纹加密固态U盘双接口锌合金外壳512GB”  联想FU100、联想FU200、移速双接口御存系列、亿捷指纹加密U盘、毕亚兹指纹加密U盘等。
;
;  不同的显示器分辨率和字体缩放显示比例会造成图像变形从而使FindText()函数无法找到相对应的灰度二值化图片,
;  此脚本在不同电脑之间无法通用,如果各位观众网友有好的解决方法希望能在评论区分享一下,谢谢。
;
;  https://www.cnblogs.com/delphixx/p/18374739
;


; 设置此脚本的运行参数
#SingleInstance force
#Persistent
#WinActivateForce
DetectHiddenWindows,On
SetTitleMatchMode,2

; 关闭并重新运行金山文档
WinClose,登录 ahk_exe 金山文档.exe
Process,Close,金山文档.exe
Run,"C:\Program Files (x86)\金山文档\金山文档.exe"

; 等待出现并点击账号密码的锁形按钮,注意请使用FindText的截屏抓图功能截图,灰度阈值我填222

Text:="|<>*222$24.000003k00Dw00yT00s701k3U1U1U3U1k300k300k7zzsDzzwQ00CM006M006M006M006M006M1k6M3k6M3U6M006M006M006M006M006Q00CDzzw7zzs0000U"
loop,100
{
	if (ok:=FindText(X, Y, 1850-150000, 1238-150000, 1850+150000, 1238+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待出现并点击同意按钮

Text:="|<>*222$41.zzzzzzzzzzzwzz003zxzyzzrU01xzzjvzDvzzTtwzo02s007jzxzzzzTzvs01ys1rrzvxrvjU07vjrTTzjrTiy00TizRxxyzQ0vqNyyvzrAtgxzziNzQvzzRs1zrzUzzzzzzzzzzy"
loop,100
{
	if (ok:=FindText(X, Y, 1762-150000, 1014-150000, 1762+150000, 1014+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待出现并点击手机号输入框
Text:="|<>*222$43.00000000k000000M00A1U0A7y60k06327zzszxX6Em0NalaAFkAnNXP0M6NgkADz3AqE661VaPQTv0kzxb1VzsNalUkkAAnMMyM66NgATg33Aq6STzVaP3/30knBVhVUMTyzYkkAA3M0MTy00A0AA3006000000000001"
loop,100
{
	if (ok:=FindText(X, Y, 2067-150000, 550-150000, 2067+150000, 550+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

Sleep,1000

; 等待出现并输入手机号和登录密码
#IfWinActive,登录 ahk_exe 金山文档.exe
; 这里需要更换成你自己的手机号
Send,138####5488
Sleep,100
Send,{tab}
Sleep,100
; 这里需要更换成你自己的金山文档的登录密码
Send,ba??.l??3##4794
#IfWinActive

; 等待出现并点击智能验证的跳动圆点

Text:="|<>*222$51.000000000000zs000000zzs00000zzzs0000Tzzzk0007zzzz0003zzzzy000zzzzzs00DzzzzzU03zzzzzy00zzzzzzs07zzzzzz01zzzzzzw0Tzzzzzzk3zzzzzzy0zzzsTzzs7zzw0zzz1zzk00DzwDzy001zzVzzk00DzwDzy001zzXzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyDzz001zzVzzs00TzwDzzU07zzVzzw00zzw7zzk0Dzz0zzzU7zzs3zzz3zzy0Tzzzzzzk1zzzzzzw07zzzzzz00zzzzzzs03zzzzzy00DzzzzzU00zzzzzs003zzzzy0007zzzz0000Tzzzk0000zzzs00001zzw000000zs000000000004"
loop,100
{
	if (ok:=FindText(X, Y, 1990-150000, 774-150000, 1990+150000, 774+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待验证成功
Sleep,3000

; 等待出现并点击立即登录按钮
Text:="|<>*222$86.zzzzzzzzzzzzzzzzTzzzzzzzzzzzzznzzzzzzwxzzzzzyTz0A0s1AzU03zzjzrvDDysTzzwz000RynnjCTTzzDzzzzTgwlbnDU03zjzjk3DD3y7zzwztznxynnt0HzzzDyTwzTgwwzzC000TnzDrvDAzzwzxzzwzbw0nnM03yzTrzDtzTgwyzyzrntztyzrzDDjzjyQwzyTDxnnnvzvzr6TzbnzTQwy00zzIDzwtzrXDDXyjzZbzzSTxknryTbzbQzzzDz1o1zrnzbrnw000VxDzwxznxz7zzzxznz000/kTzzzzzzzzzzzzzzzs"
loop,100
{
	if (ok:=FindText(X, Y, 2188-150000, 943-150000, 2188+150000, 943+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 登录成功时关闭此脚本,终止此脚本
ExitApp


;==========



FindText(ByRef x:="FindTextClass", ByRef y:="", args*)
{
	static init, obj
	if !VarSetCapacity(init) && (init:="1")
		obj:=new FindTextClass()
	return (x=="FindTextClass" && !args.Length()) ? obj : obj.FindText(x, y, args*)
}

class FindTextClass
{  ;// Class Begin

	Floor(i)
	{
		if i is number
			return i+0
		else return 0
	}

	__New()
{
	this.bits:={ Scan0: 0, hBM: 0, oldzw: 0, oldzh: 0 }
	this.bind:={ id: 0, mode: 0, oldStyle: 0 }
	this.Lib:=[]
	this.Cursor:=0
}

__Delete()
{
	if (this.bits.hBM)
		Try DllCall("DeleteObject", "Ptr",this.bits.hBM)
}

New()
{
	return new FindTextClass()
}

help()
{
	return "
	(
		;--------------------------------
		;  FindText - 屏幕找字函数
		;  版本 : 9.7  (2024-06-18)
		;--------------------------------
		;  返回变量:=FindText(
		;      OutputX --> 保存返回的X坐标的变量名称
		;    , OutputY --> 保存返回的Y坐标的变量名称
		;    , X1 --> 查找范围的左上角X坐标
		;    , Y1 --> 查找范围的左上角Y坐标
		;    , X2 --> 查找范围的右下角X坐标
		;    , Y2 --> 查找范围的右下角Y坐标
		;    , err1 --> 文字的黑点容错百分率(0.1=10%)
		;    , err0 --> 背景的白点容错百分率(0.1=10%)
		;    , Text --> 由工具生成的查找图像的数据,可以一次查找多个,用“|”分隔
		;    , ScreenShot --> 是否截屏,为0则使用上一次的截屏数据
		;    , FindAll --> 是否搜索所有位置,为0则找到一个位置就返回
		;    , JoinText --> 如果想组合查找,可以为1,或者是要查找单词的数组
		;    , offsetX --> 组合图像的每个字和前一个字的最大横向间隔
		;    , offsetY --> 组合图像的每个字和前一个字的最大高低间隔
		;    , dir --> 查找的方向,有上、下、左、右、中心9种
		;    , zoomW --> 图像宽度的缩放百分率(1.0=100%)
		;    , zoomH --> 图像高度的缩放百分率(1.0=100%)
		;  )
		;
		;  返回变量 --> 如果没找到结果会返回0。否则返回一个二级数组,
		;      第一级是每个结果对象,第二级是结果对象的具体信息对象:
		;      { 1:左上角X, 2:左上角Y, 3:图像宽度W, 4:图像高度H
		;        , x:中心点X, y:中心点Y, id:图像识别文本 }
		;  坐标都是相对于屏幕,颜色使用RGB格式
		;
		;  如果 OutputX 等于 'wait' 或 'wait1' 意味着等待图像出现,
		;  如果 OutputX 等于 'wait0' 意味着等待图像消失
		;  此时 OutputY 设置等待时间的秒数,如果小于0则无限等待
		;  如果超时则返回0,意味着失败,如果等待图像出现成功,则返回位置数组
		;  如果等待图像消失成功,则返回 1
		;  例1: FindText(X:='wait', Y:=3, 0,0,0,0,0,0,Text)   ; 等待3秒等图像出现
		;  例2: FindText(X:='wait0', Y:=-1, 0,0,0,0,0,0,Text) ; 无限等待等图像消失
		;
		;  <FindMultiColor> 或 <FindColor> : 找色 是仅有一个点的 多点找色
		;  Text:='|<>##DRDGDB $ 0/0/RRGGBB1-DRDGDB1/RRGGBB2, xn/yn/-RRGGBB3/RRGGBB4, ...'
		;  '##'之后的颜色 (0xDRDGDB) 是所有颜色的默认偏色(各个分量允许的变化值)
		;  初始点 (0,0) 匹配 0xRRGGBB1(+/-0xDRDGDB1) 或者 0xRRGGBB2(+/-0xDRDGDB),
		;  点 (xn,yn) 匹配 排除 0xRRGGBB3(+/-0xDRDGDB) 和排除 0xRRGGBB4(+/-0xDRDGDB)
		;  点坐标后面以 '-' 开头表示要排除后面的所有颜色,其他颜色都匹配
		;  每个点最多允许匹配10组颜色 (xn/yn/RRGGBB1/.../RRGGBB10)
		;
		;  <FindPic> : Text 参数需要手动输入
		;  Text:='|<>##DRDGDB-RRGGBB1-RRGGBB2... $ d:\a.bmp'
		;  0xRRGGBB1(+/-0xDRDGDB)... 都是透明色,不参与匹配
		;
		;--------------------------------
	)"
}

FindText(ByRef OutputX:="", ByRef OutputY:=""
	, x1:=0, y1:=0, x2:=0, y2:=0, err1:=0, err0:=0, text:=""
	, ScreenShot:=1, FindAll:=1, JoinText:=0, offsetX:=20, offsetY:=10
	, dir:=1, zoomW:=1, zoomH:=1)
{
	local
	if (OutputX ~= "i)^\s*wait[10]?\s*$")
	{
		found:=!InStr(OutputX,"0"), time:=this.Floor(OutputY)
			, timeout:=A_TickCount+Round(time*1000), OutputX:=""
		loop
		{
			ok:=this.FindText(,, x1, y1, x2, y2, err1, err0, text, ScreenShot
				, FindAll, JoinText, offsetX, offsetY, dir, zoomW, zoomH)
			if (found && ok)
			{
				OutputX:=ok[1].x, OutputY:=ok[1].y
				return ok
			}
			if (!found && !ok)
				return 1
			if (time>=0 && A_TickCount>=timeout)
				break
			Sleep 50
		}
		return 0
	}
	SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
	x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
	if (x1=0 && y1=0 && x2=0 && y2=0)
		n:=150000, x:=y:=-n, w:=h:=2*n
	else
		x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
	bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
		, this.ok:=0, info:=[]
	loop Parse, text, |
		if IsObject(j:=this.PicInfo(A_LoopField))
			info.Push(j)
	if (w<1 || h<1 || !(num:=info.Length()) || !bits.Scan0)
	{
		SetBatchLines % bch
		return 0
	}
	arr:=[], info2:=[], k:=0, s:=""
		, mode:=(IsObject(JoinText) ? 2 : JoinText ? 1 : 0)
	For i,j in info
	{
		k:=Max(k, (j[7]=5 && j[8]=0 ? j[9] : j[2]*j[3]))
		if (mode)
			v:=(mode=1 ? i : j[10]) . "", (mode=1 && s.="|" v)
			, (!info2.HasKey(v) && info2[v]:=[]), (v!="" && info2[v].Push(j))
	}
	sx:=x, sy:=y, sw:=w, sh:=h
		, (mode=1 && JoinText:=[s])
		, VarSetCapacity(s1,k*4), VarSetCapacity(s0,k*4), VarSetCapacity(ss,sw*(sh+3))
		, allpos_max:=(FindAll || JoinText ? 10240 : 1)
		, ini:={ sx:sx, sy:sy, sw:sw, sh:sh, zx:zx, zy:zy
		, mode:mode, bits:bits, ss:&ss, s1:&s1, s0:&s0
		, err1:err1, err0:err0, allpos_max:allpos_max
		, zoomW:zoomW, zoomH:zoomH }
	loop 2
	{
		if (err1=0 && err0=0) && (num>1 || A_Index>1)
			ini.err1:=err1:=0.05, ini.err0:=err0:=0.05
		if (!JoinText)
		{
			VarSetCapacity(allpos, allpos_max*4), allpos_ptr:=&allpos
			For i,j in info
				loop % this.PicFind(ini, j, dir, sx, sy, sw, sh, allpos_ptr)
				{
					pos:=NumGet(allpos, 4*(A_Index-1), "uint")
						, x:=(pos&0xFFFF)+zx, y:=(pos>>16)+zy
						, w:=Floor(j[2]*zoomW), h:=Floor(j[3]*zoomH), comment:=j[10]
						, arr.Push({1:x, 2:y, 3:w, 4:h, x:x+w//2, y:y+h//2, id:comment})
					if (!FindAll)
						break 3
				}
		}
		else
			For k,v in JoinText
			{
				v:=StrSplit(Trim(RegExReplace(v, "\s*\|[|\s]*", "|"), "|")
					, (InStr(v,"|")?"|":""), " `t")
					, this.JoinText(arr, ini, info2, v, 1, offsetX, offsetY
					, FindAll, dir, 0, 0, 0, sx, sy, sw, sh)
				if (!FindAll && arr.Length())
					break 2
			}
		if (err1!=0 || err0!=0 || arr.Length() || info[1][4] || info[1][7]=5)
			break
	}
	SetBatchLines % bch
	if (arr.Length())
	{
		OutputX:=arr[1].x, OutputY:=arr[1].y, this.ok:=arr
		return arr
	}
	return 0
}

; 组合文本参数可以用数组 <==> [ "abc", "xyz", "a1|a2|a3" ]

JoinText(arr, ini, info2, text, index, offsetX, offsetY
	, FindAll, dir, minX, minY, maxY, sx, sy, sw, sh)
{
	local
	if !(Len:=text.Length()) || !info2.HasKey(key:=text[index])
		return 0
	VarSetCapacity(allpos, ini.allpos_max*4), allpos_ptr:=&allpos
		, zoomW:=ini.zoomW, zoomH:=ini.zoomH, mode:=ini.mode
	For i,j in info2[key]
		if (mode!=2 || key==j[10])
			loop % this.PicFind(ini, j, dir, sx, sy, (index=1 ? sw
: Min(sx+offsetX+Floor(j[2]*zoomW),ini.sx+ini.sw)-sx), sh, allpos_ptr)
	{
		pos:=NumGet(allpos, 4*(A_Index-1), "uint")
			, x:=pos&0xFFFF, y:=pos>>16
			, w:=Floor(j[2]*zoomW), h:=Floor(j[3]*zoomH)
			, (index=1 && (minX:=x, minY:=y, maxY:=y+h))
			, minY1:=Min(y, minY), maxY1:=Max(y+h, maxY), sx1:=x+w
		if (index<Len)
		{
			sy1:=Max(minY1-offsetY, ini.sy)
				, sh1:=Min(maxY1+offsetY, ini.sy+ini.sh)-sy1
			if this.JoinText(arr, ini, info2, text, index+1, offsetX, offsetY
				, FindAll, 5, minX, minY1, maxY1, sx1, sy1, 0, sh1)
				&& (index>1 || !FindAll)
				return 1
		}
		else
		{
			comment:=""
			For k,v in text
				comment.=(mode=2 ? v : info2[v][1][10])
			x:=minX+ini.zx, y:=minY1+ini.zy, w:=sx1-minX, h:=maxY1-minY1
				, arr.Push({1:x, 2:y, 3:w, 4:h, x:x+w//2, y:y+h//2, id:comment})
			if (index>1 || !FindAll)
				return 1
		}
	}
return 0
}

PicFind(ini, j, dir, sx, sy, sw, sh, allpos_ptr)
{
	local
	static init, MyFunc
	if !VarSetCapacity(init) && (init:="1")
	{
		x32:="VVdWU4HskAAAAIuEJKQAAACLvCSoAAAAi6wk3AAAAMcEJAAAAACD6AGD+AQPh48F"
		. "AACDvCSkAAAABQ+EkQUAAIuEJOAAAACFwA+ONA0AADHAibwkqAAAAMcEJAAAAADH"
		. "RCQUAAAAAMdEJAwAAAAAicfHRCQYAAAAAI20JgAAAACLhCTYAAAAi0wkGDH2MdsB"
		. "yIXtiUQkCH896ZAAAABmkA+vhCTEAAAAicGJ8Jn3@QHBi0QkCIA8GDF0TIuEJNQA"
		. "AACDwwEDtCT4AAAAiQy4g8cBOd10VIsEJJn3vCTgAAAAg7wkpAAAAAR1tQ+vhCS4"
		. "AAAAicGJ8Jn3@Y0MgYtEJAiAPBgxdbSLRCQMi5Qk0AAAAIPDAQO0JPgAAACJDIKD"
		. "wAE53YlEJAx1rAFsJBiDRCQUAYuMJPwAAACLRCQUAQwkOYQk4AAAAA+FMv@@@4tM"
		. "JAy7rYvbaIn+D6+MJOQAAACJfCQ0i7wkqAAAAInIwfkf9+vB+gwpyouMJOgAAACJ"
		. "VCRID6@OicjB+R@368H6DCnKiVQkTIO8JKQAAAAED4TGCAAAi4QkzAAAAAOEJMQA"
		. "AACLtCS8AAAAi4wkuAAAAIlEJCiLhCS4AAAAD6+EJMAAAACNNLCLhCTEAAAA99iD"
		. "vCSkAAAAAY0EgYlEJDgPhKAJAACDvCSkAAAAAg+EIgcAAIuUJMgAAACF0g+O9wcA"
		. "AIuMJMQAAACLRCQoifXHBCQAAAAAx0QkCAAAAACJvCSoAAAAjQRIiUQkHInIweAC"
		. "iUQkFIuEJMQAAACFwH5ai4wktAAAAItcJByLvCS0AAAAA1wkCAHpA2wkFAHvjXYA"
		. "D7ZRAoPBBIPDAWvyJg+2Uf1rwkuNFAYPtnH8ifDB4AQp8AHQwfgHiEP@Ofl10ou0"
		. "JMQAAAABdCQIgwQkAQNsJDiLBCQ5hCTIAAAAdYeLhCTEAAAAi5QkrAAAADHtMfaD"
		. "6AGJRCQUi4QkyAAAAIPoAYlEJBiLhCTEAAAAhcAPjuIAAACLvCTEAAAAi0QkHAH3"
		. "jQwwifuJfCQgiccB2InzK5wkxAAAAIkEJDHAAfuLfCQoAfeLNCSJHCSJfCQIjXYA"
		. "hcAPhJQQAAA5RCQUD4SKEAAAhe0PhIIQAAA5bCQYD4R4EAAAD7YRD7Z5@7sBAAAA"
		. "A5QkqAAAADn6ckUPtnkBOfpyPYs8JA+2Pzn6cjMPtj45+nIsizwkD7Z@@zn6ciGL"
		. "PCQPtn8BOfpyFg+2fv85+nIOD7ZeATnaD5LDkI10JgCLfCQIiBwHg8ABg8EBg8YB"
		. "gwQkATmEJMQAAAAPhV@@@@+LdCQgg8UBOawkyAAAAA+F@@7@@4u8JKgAAACJlCSs"
		. "AAAAi4QkxAAAAMdEJBQAAAAAx0QkGAAAAACJvCSoAAAAg+gBiUQkCJCNtCYAAAAA"
		. "i4QkxAAAAIXAD468BQAAi0QkGItcJCgxyYuUJMwAAAC@AwAAAAHDAdAPtjOJBCTr"
		. "aY1W@7gCAAAAg@oBdhOD@wEPlMKD@QEPlMAJ0A+2wAHAgeb9AAAAugEAAAB0EoX@"
		. "D5TChe2J1w+UwonWCfeJ+os8JAnQiAQPg8EBOYwkxAAAAA+EOAUAAIXJD7ZzAQ+E"
		. "PA8AAA+2O4PDATtMJAgPhFkOAAAPtmsBi4Qk7AAAAIXAD4V6@@@@jUb@g@gCGcCD"
		. "4AKB5v0AAAAPlMLro4t0JCiLbCQsx0QkbAAAAADHRCRoAQAAAMdEJEQAAAAAx0Qk"
		. "YAAAAACJ8I1eAY1VAcHoH4lsJDjHBCQAAAAAAfDR+IlEJBiJ6MHoHwHo0fiJRCQc"
		. "ifCJ3g+v8o1ICYl0JHyJxonog8AJOdOJdCQ8D0@Bi3QkfInBD6@IOXQkbImMJIAA"
		. "AAB9TYu0JIAAAAA5dCRgx0QkZAAAAAB9OItMJGg5TCRkD4wRCAAAi3QkRINEJGAB"
		. "ifCD4AEBwYnwi3QkfIPAAYlMJGiD4AM5dCRsiUQkRHyziwQkgcSQAAAAW15fXcJc"
		. "ADHAhf8PlcCJRCRcD4TNBAAAi4Qk4AAAAIu0JNgAAACJ+cHpEIn7D7bJD7bTD6@F"
		. "jQSGic4Pr@GJRCQkifgPtsSJdCQwicYPr@CJ0A+vwol0JASJRCQQi4Qk4AAAAIXA"
		. "D466CAAAi3QkJI0ErQAAAACJ+4msJNwAAACLvCSsAAAAi2wkMIlEJDwxwMdEJCwA"
		. "AAAAx0QkNAAAAADHRCQMAAAAAIk0JIu0JNwAAACF9g+OGAEAAIuMJNgAAACLNCTH"
		. "RCQYAAAAAIlcJCABwQNEJDyJTCQUiUQkOAOEJNgAAACJRCQoi0QkFIX@D7ZQAQ+2"
		. "SAIPtgCJFCSJRCQIdEQx0osclonYwegQD7bAKcgPr8A5xXwjD7bHKwQkD6@AOUQk"
		. "BHwUD7bDK0QkCA+vwDlEJBAPjRkDAACDwgE5+nXCiVwkIItEJAzB4RDB4AKJRCQc"
		. "i0QkLJn3vCTgAAAAD6+EJLgAAACJw4tEJBiZ97wk3AAAAItUJAyNBIOLnCTQAAAA"
		. "iQSTiwQkg8IBi5wk1AAAAIlUJAzB4AgJwQtMJAiLRCQciQwDg0QkFASLlCT4AAAA"
		. "i0QkFAFUJBg7RCQoD4Ui@@@@i1wkIItEJDiJNCSDRCQ0AYu0JPwAAACLTCQ0AXQk"
		. "LDmMJOAAAAAPhbj+@@+LTCQMuq2L22iJ3w+vjCTkAAAAx0QkFAAAAADHRCQ0AAAA"
		. "AInIwfkf9+rB+gwpyolUJAiLdCQMi0wkCDHAOc6LTCQUD07wiXQkDIt0JDQ5zg9P"
		. "xolEJDSLhCTEAAAAK4Qk+AAAAIlEJCiLhCTIAAAAK4Qk@AAAAIO8JKQAAAADiUQk"
		. "LA+O4gEAAIuMJMQAAACLhCTMAAAAD6+MJMgAAACJygHChcl+CsYAAYPAATnCdfaD"
		. "vCSwAAAACQ+EcPz@@4uEJLAAAACD6AGD+AcPh64BAACD+AOJRCRAD46pAQAAi0Qk"
		. "KMdEJFAAAAAAxwQkAAAAAIlEJDiLRCQsiUQkPIt0JFA5dCQ4x0QkWAAAAAAPjO@8"
		. "@@+LdCRYOXQkPA+M9wUAAItMJFCLdCRAi0QkOCnI98YCAAAAD0TBi0wkWInCi0Qk"
		. "PCnI98YBAAAAD0TBg@4DidEPT8gPT8KJTCQciUQkGOnBBAAAjUcBxwQkAAAAAMdE"
		. "JAgAAAAAweAHiceLhCTEAAAAweACiUQkGIuEJMgAAACFwA+OsAAAAIuEJMQAAACF"
		. "wH5ci4wktAAAAItcJCiLrCS0AAAAA1wkCAHxA3QkGIl0JBQB9Q+2UQIPtkEBD7Yx"
		. "a8BLa9ImAcKJ8MHgBCnwAdA5xw+XA4PBBIPDATnpddWLjCTEAAAAAUwkCIt0JBSD"
		. "BCQBA3QkOIsEJDmEJMgAAAB1hekb+v@@kI20JgAAAACLtCTEAAAAAXQkGINEJBQB"
		. "i0QkFDmEJMgAAAAPjx@6@@+LvCSoAAAAi0QkSIlEJAiLRCRMiUQkFOnU@f@@jXYA"
		. "iVwkIOlL@f@@g7wk7AAAAAGDnCS8AAAA@+kx@v@@x0QkQAAAAACLRCQsx0QkUAAA"
		. "AADHBCQAAAAAiUQkOItEJCiJRCQ86VL+@@+J+MHoEA+vhCT8AAAAmfe8JOAAAAAP"
		. "r4QkuAAAAInBD7fHD6+EJPgAAACZ9@2NPIGLRCRIiUQkCItEJEyJRCQU6UL9@@+L"
		. "hCSsAAAAhcAPhCMEAACLjCSsAAAAi7Qk0AAAAIuEJNQAAACLnCTYAAAAiawk3AAA"
		. "AI08jjHJicWJfCQIizuDxgSDw1iDxQSJ+MHoEA+vhCT8AAAAmfe8JOAAAAAPr4Qk"
		. "uAAAAIkEJA+3xw+vhCT4AAAAmfe8JNwAAACLFCSNBIKJRvyLQ6yNBEGDwRaJRfw5"
		. "dCQIdaeLhCSsAAAAi4wk5AAAALqti9toD6@IiUQkDInIwfkf9+qJ0MH4DCnIi7Qk"
		. "2AAAAIlEJAjHRCQUAAAAAMdEJDQAAAAAg8YIiXQkJOld@P@@i4Qk4AAAAIucJMgA"
		. "AADRpCSsAAAAx0QkLAAAAADHRCQ8AAAAAA+vxQOEJNgAAACJRCQki4QkxAAAAMHg"
		. "AoXbiUQkVA+OK@7@@4m8JKgAAACLfCRci4wkxAAAAIXJD474AAAAi0QkKANEJDyL"
		. "nCS0AAAAiceLRCRUAfOJ3QHwif6JRCRAA4QktAAAAIlEJCDrE8YGAIPFBIPGATts"
		. "JCAPhKgAAAAPtkUCMf+JBCQPtkUBiUQkFA+2RQCJRCQYObwkrAAAAHbLi0QkJIsU"
		. "uIPHAotMuPwPtt4rXCQUidDB6BAPtsArBCSJXCQID7baK1wkGIH6@@@@AIlcJBwP"
		. "hosAAACLFCSNHFCNkwAEAAAPr9APr8KLVCQID6@SweILAdC6@gUAACnaidOLVCQc"
		. "D6@aD6@ajRQYOdFyhMYGAYPFBIPGATtsJCAPhVj@@@+LjCTEAAAAAUwkPIt0JECD"
		. "RCQsAQN0JDiLRCQsOYQkyAAAAA+F3@7@@4l8JFyLvCSoAAAA6dr2@@+NtCYAAAAA"
		. "icrB6hAPttoPttUPtsmJVCQEidqJXCQwD6@AiUwkEA+v0znQD48H@@@@i1wkCItM"
		. "JASJ2InKD6@DD6@ROdAPj+3+@@+LVCQci0wkEInTicgPr9oPr8E5ww+P0@7@@+lK"
		. "@@@@x0QkTAAAAADHRCRIAAAAAMdEJDQAAAAAx0QkDAAAAADp7vP@@4tEJBiFwA+I"
		. "5gAAAIt0JDw58A+P2gAAAItEJByFwA+IzgAAAIt0JDg58A+PwgAAAINEJGwBx0Qk"
		. "QAkAAACLRCQci7QkzAAAAA+vhCTEAAAAA0QkGIA8BgAPhIMAAACLTCQ0i3QkDDnx"
		. "D03xg7wkpAAAAAOJdCQgD48UAQAAhfYPhAQEAAADhCTMAAAAi1wkFDHSi3QkCInB"
		. "6yo5VCQ0fheLrCTUAAAAi0SVAAHI9gABdQWD6wF4KoPCATlUJCAPhMUDAAA7VCQM"
		. "fdCLrCTQAAAAi0SVAAHIgDgBd76D7gF5uYN8JEAJdAqDRCRYAek4+v@@i2wkRIXt"
		. "dC6DfCREAQ+EFQUAAIN8JEQCD4QABQAAMcCDfCREAw+UwClEJBiDRCRkAem59v@@"
		. "g2wkHAHr74NEJFAB6dv5@@8xwMdEJAwAAAAA6X38@@+LhCTEAAAAK4Qk+AAAAMdE"
		. "JAgAAAAAx0QkNAAAAADHRCQUAAAAAMdEJAwAAAAAiUQkKIuEJMgAAAArhCT8AAAA"
		. "iUQkLOkO+f@@i0QkHAOEJMAAAAAPr4QkuAAAAItUJBgDlCS8AAAAg7wkpAAAAAWN"
		. "BJCJRCRID4RTAQAAi7QktAAAAAH4i1QkIA+2dAYChdKJdCRMi7QktAAAAA+2dAYB"
		. "iXQkVIu0JLQAAAAPtgQGiUQkcA+EiAIAAItEJBSJvCSoAAAAMe2J94lEJHiLRCQI"
		. "iUQkdOt+jXYAjbwnAAAAADlsJDR+YYuEJNQAAACLVCRIi1wkTAMUqA+2TBcCD7ZE"
		. "FwErRCRUD7YUFytUJHCJzgHZKd6NmQAEAAAPr8APr97B4AsPr94Bw7j+BQAAKcgP"
		. "r8IPr8IBwzmcJKwAAAByB4NsJHgBeHyDxQE5bCQgD4TmAQAAO2wkDH2Gi4Qk0AAA"
		. "AItUJEiLXCRMAxSoD7ZMFwIPtkQXAStEJFQPthQXK1QkcInOAdkp3o2ZAAQAAA+v"
		. "wA+v3sHgCw+v3gHDuP4FAAApyA+vwg+vwgHDOZwkrAAAAA+DKP@@@4NsJHQBD4kd"
		. "@@@@i7wkqAAAAOnK@f@@i3QkXIX2D4VcAgAAi1wkIIXbD4RaAQAAi4Qk0AAAAItc"
		. "JCQx9om8JKgAAACJRCRMi4Qk1AAAAIlEJFRrRCQgFolEJHiLRCQIiUQkdIt8JEyL"
		. "RCRIifWJ2YlcJHADB4s7iXwkIIt8JFSLP4m8JKwAAACLvCS0AAAAD7Z8BwKJvCSE"
		. "AAAAi7wktAAAAA+2fAcBibwkiAAAAIu8JLQAAAAPtgQHiYQkjAAAAOl9AAAAjXYA"
		. "iwGLeQSDxQLB7xCJwsHqEIn7D7b7D7bSi1kEK5QkhAAAAIl8JDAPr@8Ptt+JXCQE"
		. "D7ZZBA+v0jn6iVwkEH84i1wkBA+21CuUJIgAAACJ3w+v0g+v+zn6fx6LXCQQD7bA"
		. "K4QkjAAAAInaD6@AD6@TOdAPjvIBAACDwQg7rCSsAAAAD4J5@@@@gXwkIP@@@wCL"
		. "XCRwdwuDbCR0AQ+Io@7@@4NEJEwEg8NYg0QkVASDxhY5dCR4D4Xh@v@@i7wkqAAA"
		. "AIuEJPAAAACDBCQBhcB0NotUJBwDlCTAAAAAi0QkGAOEJLwAAACLNCSLjCTwAAAA"
		. "weIQCdA7tCT0AAAAiUSx@A+NS@P@@4t0JBwxyYnwK4Qk@AAAAIPAAYnCifCLtCT8"
		. "AAAAD0jRjUQw@4t0JCw58A9Pxot0JBiJxYnwK4Qk+AAAAIPAAYnDifCLtCT4AAAA"
		. "D0jZjUQw@4t0JCg5xg9OxjnqicYPj6@7@@+LhCTEAAAAD6@CA4QkzAAAAInBjUUB"
		. "jW4BiUQkIDnzfw2J2MYEAQCDwAE56HX1g8IBA4wkxAAAADtUJCB13+ls+@@@vQMA"
		. "AADpofH@@4tMJCCFyQ+E@v7@@4tsJAgx9ou8JNAAAACLRCRIAwS3i7wk1AAAAIsc"
		. "t4u8JLQAAAAPtkwHAonaweoQD7bSKdEPtlQHAQ+2@w+vySn6i7wktAAAAA+2BAcP"
		. "tvsp+DlMJDB8Eg+v0jlUJAR8CQ+vwDlEJBB9BYPtAXgQg8YBOXQkIHWPid@pgP7@"
		. "@4nf6dH6@@+DRCQcAekE+@@@g0QkGAHp+vr@@4t8JAjGBAcC6d7v@@+BfCQg@@@@"
		. "AItcJHAPhxr+@@@pIP7@@78DAAAA6b3w@@+QkJCQkJA="
		x64:="QVdBVkFVQVRVV1ZTSIHsqAAAAEUx5ESLrCQwAQAAi5wkYAEAAImMJPAAAACLhCTw"
		. "AAAAidFEiYQkAAEAAESJjCQIAQAAi7wkaAEAAIPoAYP4BA+HxwUAAIO8JPAAAAAF"
		. "D4TQBQAAhf8PjgoNAABEiXwkMESLnCTwAAAARTHkTIu8JFABAACLrCSYAQAAMfZF"
		. "MfbHRCQcAAAAAESJVCQgRIlkJBCJlCT4AAAADx9EAABMY1QkHEUxyUUxwEwDlCRY"
		. "AQAAhdt@Net6Dx+AAAAAAEEPr8WJwUSJyJn3+wHBQ4A8AjF0PEmDwAFJY8ZBAelB"
		. "g8YBRDnDQYkMh35DifCZ9@9Bg@sEdckPr4QkGAEAAInBRInImff7Q4A8AjGNDIF1"
		. "xEiLlCRIAQAASYPAAUljxEEB6UGDxAFEOcOJDIJ@vQFcJByDRCQQAQO0JKABAACL"
		. "RCQQOccPhVX@@@9FieBBua2L22hEi1QkIEQPr4QkcAEAAESLfCQwi4wk+AAAAESJ"
		. "ZCQsRInAQcH4H0H36YnQwfgMRCnARIuEJHgBAACJRCRMRQ+vxkSJwEHB+B9B9+nB"
		. "+gxEKcKJVCRQg7wk8AAAAAQPhJcIAABJY+2LtCQgAQAASInoSAOEJEABAABIiUQk"
		. "OIuEJBgBAAAPr4QkKAEAAI0EsIu0JBgBAACJRCQgRIno99iDvCTwAAAAAY0EholE"
		. "JEAPhGEJAACDvCTwAAAAAg+EWgcAAESLjCQ4AQAARYXJD473AgAAQ41EbQBEiXQk"
		. "HESLdCQgRo0krQAAAAAx9jH@SJhIA4QkQAEAAEiJRCQQRYXtfllIi5wkEAEAAElj"
		. "xkUxyUyNRAMCSGPfSANcJBBBD7YQSYPABERr2iZBD7ZQ+2vCS0GNFANFD7ZY+kSJ"
		. "2MHgBEQp2AHQwfgHQogEC0mDwQFFOc1@yEUB5kQB74PGAUQDdCRAObQkOAEAAHWR"
		. "SI1FAUSLdCQci5QkAAEAADHbx0QkHAAAAABIiUQkILgBAAAASCnoQY1t@0SJdCRA"
		. "SIlEJDCLhCQ4AQAARI1g@0WF7Q+OzgAAAEhjdCQcSIt8JCBIi0QkEEyNHDdIi3wk"
		. "MEyNRDABSQHDTI0MN0iLfCQ4SQHBMcBMjTQ3Dx9EAABIhcAPhOoQAAA5xQ+E4hAA"
		. "AIXbD4TaEAAAQTncD4TREAAAQQ+2UP9BD7Zw@r8BAAAAAco58nI+QQ+2MDnycjZB"
		. "D7Zx@znyci1BD7Zz@znyciRBD7Zx@jnychtBD7YxOfJyE0EPtnP+OfJyCkEPtjM5"
		. "8kAPksdBiDwGSIPAAUmDwAFJg8MBSYPBAUE5xQ+Pbv@@@0QBbCQcg8MBOZwkOAEA"
		. "AA+FGf@@@0SLdCRAiZQkAAEAAESJdCQQRIl8JCBBjW3@TIt0JDhEi7wkOAEAAEUx"
		. "5Iu0JIABAAAx@0SJVCQciYwk+AAAAGYuDx+EAAAAAABFhe0PjsIAAABMY88xyUG7"
		. "AwAAAEcPthQOT41EDgFMA4wkQAEAAOttDx+EAAAAAABBjVL@uAIAAACD+gF2FEGD"
		. "+wEPlMKD+wEPlMAJ0A+2wAHAQYHi@QAAALoBAAAAdA9FhdtBD5TChdsPlMJECdKD"
		. "wQEJ0EE5zUGIAXRMhclFD7YQD4SfDwAARQ+2WP9Jg8ABSYPBATnpD4SmDgAAQQ+2"
		. "GIX2dYtBjUL@g@gCGcCD4AJBgeL9AAAAD5TCg8EBCdBBOc1BiAF1tEQB70GDxAFF"
		. "OecPjyj@@@9Ei3QkEESLVCQcRIt8JCCLjCT4AAAAi0QkTIlEJBCLRCRQiUQkHOkV"
		. "AwAAi3wkQItcJDjHRCR4AAAAAMdEJHQBAAAAx0QkSAAAAADHRCRsAAAAAIn4RI1P"
		. "AY1TAcHoH4lcJEQB+NH4iUQkIInYwegfAdjR+IlEJDCJ+ESJzw+v+kSNQAlBOdGJ"
		. "vCSIAAAAiceNQwmJfCRMi7wkiAAAAEEPT8BFMeSJxg+v8Dl8JHiJtCSMAAAAfVCL"
		. "vCSMAAAAOXwkbMdEJHAAAAAAfTuLXCR0OVwkcA+MpgcAAIt8JEiDRCRsAYn4g+AB"
		. "AcOJ+Iu8JIgAAACDwAGJXCR0g+ADOXwkeIlEJEh8sESJ4EiBxKgAAABbXl9dQVxB"
		. "XUFeQV@DMcCF0g+VwIlEJGgPhFkEAACJ+EGJ0g+20g+vw0HB6hBFD7bSweACSJhI"
		. "A4QkWAEAAEUPr9JIiUQkCA+2xUGJx0QPr@iJ0A+vwoX@iUQkKA+OZAgAAIu0JAAB"
		. "AACNQ@+JvCRoAQAAi3wkKESJrCQwAQAASI0EhQYAAADHRCQcAAAAAMdEJCAAAAAA"
		. "jVb@SIt0JAjHRCQsAAAAAEiJRCQwjQSdAAAAAImcJGABAABIjXSWBDHSiUQkQEGJ"
		. "1YusJGABAACF7Q+O+QAAAEhjRCQsSIucJFgBAAAx7UiNXAMCSANEJDBIA4QkWAEA"
		. "AEiJRCQQZi4PH4QAAAAAAESLpCQAAQAARA+2A0QPtkv@RA+2W@5FheR0PkiLVCQI"
		. "iwqJyMHoEA+2wEQpwA+vwEE5wnwbD7bFRCnID6@AQTnHfA0PtsFEKdgPr8A5x31b"
		. "SIPCBEg58nXHi0QkHE1j9UHB4BBBweEIQYPFAUUJyJlFCdj3vCRoAQAAD6+EJBgB"
		. "AABBicSJ6Jn3vCRgAQAASIuUJEgBAABBjQSEQokEskiLhCRQAQAARokEsEiDwwQD"
		. "rCSYAQAASDtcJBAPhT@@@@+LXCRAAVwkLINEJCABi5QkoAEAAItEJCABVCQcOYQk"
		. "aAEAAA+F1@7@@0SJbCQsRItEJCy6rYvbaEQPr4QkcAEAAESLrCQwAQAAx0QkHAAA"
		. "AABFMfZEicBBwfgf9+rB+gxEKcKJVCQQi3wkLItcJBAxwDnfD074RDt0JByJfCQs"
		. "RA9O8ESJ6CuEJJgBAACJRCRAi4QkOAEAACuEJKABAACDvCTwAAAAA4lEJDgPjn8B"
		. "AACLlCQ4AQAAQQ+v1YXSfhxIi4QkQAEAAIPqAUiNVBABxgABSIPAAUg5wnX0g7wk"
		. "CAEAAAkPhGj8@@+LhCQIAQAAg+gBg@gHD4dIAQAAg@gDiUQkUA+OQwEAAItEJEDH"
		. "RCRUAAAAAEUx5IlEJESLRCQ4iUQkTIt8JFQ5fCREx0QkZAAAAAAPjPP8@@+LfCRk"
		. "OXwkTA+MlgUAAItcJFSLfCRQi0QkRCnYQPbHAg9Ew4tcJGSJwotEJEwp2ED2xwEP"
		. "RMOD@wOJ0w9P2A9PwolcJDCJRCQg6VsEAABmDx9EAABEi4QkOAEAAIPBATH2weEH"
		. "Mf9CjSytAAAAAESLZCQgRYXAD46G+@@@RYXtfl5Ii5wkEAEAAEljxEUxyUyNRAMC"
		. "SGPfSANcJDgPH4QAAAAAAEEPthBBD7ZA@0UPtlj+a8BLa9ImAcJEidjB4AREKdgB"
		. "0DnBQg+XBAtJg8EBSYPABEU5zX@LQQHsRAHvg8YBRANkJEA5tCQ4AQAAdYzp4vn@"
		. "@4O8JIABAAABg5wkIAEAAP@pl@7@@8dEJFAAAAAAi0QkOMdEJFQAAAAARTHkiUQk"
		. "RItEJECJRCRM6bj+@@+JyMHoEA+vhCSgAQAAmff@D6+EJBgBAABBicAPt8EPr4Qk"
		. "mAEAAJn3+0GNDICLRCRMiUQkEItEJFCJRCQc6bj9@@9Ei7QkAAEAAEUxyTH2TIuc"
		. "JFgBAABFhfYPhCIEAABIi6wkSAEAAEyLpCRQAQAARIu0JKABAABBiwtJg8NYicjB"
		. "6BBBD6@Gmff@D6+EJBgBAABBicAPt8EPr4QkmAEAAJn3+0GNBIBCiUSNAEGLQ6yN"
		. "BEaDxhZDiQSMSYPBAUQ5jCQAAQAAd6+LhCQAAQAARIuEJHABAAC6rYvbaEQPr8CJ"
		. "RCQsRInAQcH4H@fqidDB+AxEKcBIi7wkWAEAAIlEJBBFMfbHRCQcAAAAAEiDxwhI"
		. "iXwkCOnf@P@@ifiLtCQ4AQAA0aQkAAEAAA+vw0iYSAOEJFgBAACF9kiJRCQID46L"
		. "+f@@QY1F@0SLXCRoRIukJAABAADHRCQwAAAAAMdEJEQAAAAASI0EhQYAAABEiXQk"
		. "fESJfCQciYwk+AAAAESJrCQwAQAASIlEJFhCjQStAAAAAIlEJGCLnCQwAQAAhdsP"
		. "jjIBAABIY0QkIEiLvCQQAQAASGN0JERIA3QkOEyNTAcCSANEJFhIAfhIiUQkEOsW"
		. "xgYASYPBBEiDxgFMO0wkEA+E3gAAAEEPtilFD7Zx@0Ux20UPtmn+SItcJAhFOeNz"
		. "z4sTQYPDAotLBInQD7b+RA+2wsHoEEQp90Up6A+2wCnogfr@@@8AdjmNFGgPr@9E"
		. "jboABAAARA+v+MHnC0EPr8cBx7j+BQAAKdCJwkEPr9CJ0EEPr8AB+DnBc1JIg8MI"
		. "65pBicoPttUPtslBweoQQYnPiUwkKEUPttKJVCQcRInRD6@AQQ+vyjnIf9CLVCQc"
		. "D6@@idAPr8I5x3@ARInARIn6QQ+vwEEPr9c50H+uxgYBSYPBBEiDxgFMO0wkEA+F"
		. "Iv@@@4t8JGABfCQgi7wkMAEAAAF8JESDRCQwAYtcJECLRCQwAVwkIDmEJDgBAAAP"
		. "haH+@@9EiVwkaESLdCR8RIt8JByLjCT4AAAARIusJDABAADpi@b@@8dEJFAAAAAA"
		. "x0QkTAAAAABFMfbHRCQsAAAAAOkh9P@@i0QkIIXAD4jvAAAAi3wkTDn4D4@jAAAA"
		. "i0QkMIXAD4jXAAAAi3wkRDn4D4@LAAAAg0QkeAHHRCRQCQAAAItEJDCLfCQgQQ+v"
		. "xUSNDDhIi7wkQAEAAEljwYA8BwAPhIgAAACLRCQsRDnwQQ9MxoO8JPAAAAADicYP"
		. "jxoBAACFwA+EYgQAAESLXCQci1wkEEUxwEiJ+OsvZpBBOdZ+G0iLvCRQAQAARInK"
		. "QgMUh@YEEAF1BkGD6wF4MkmDwAFEOcYPjiIEAABEOUQkLESJwn7JSIusJEgBAABE"
		. "ic9CA3yFAIA8OAF3s4PrAXmug3wkUAl0CoNEJGQB6Zn6@@+LVCRIhdJ0LoN8JEgB"
		. "D4SHBQAAg3wkSAIPhHIFAAAxwIN8JEgDD5TAKUQkIINEJHAB6Rv3@@+DbCQwAevv"
		. "g0QkVAHpPPr@@2YPH0QAADHAx0QkLAAAAADpZfz@@0SJ6CuEJJgBAADHRCQsAAAA"
		. "AEUx9sdEJBAAAAAAx0QkHAAAAACJRCRAi4QkOAEAACuEJKABAACJRCQ46XP5@@+L"
		. "RCQwA4QkKAEAAA+vhCQYAQAAi1QkIAOUJCABAACDvCTwAAAABQ+EkgEAAI0ckEiL"
		. "vCQQAQAAhfaNBBmNUAJIY9IPtiwXjVABSJhIY9IPtjwXiXwkWEiLvCQQAQAAD7YE"
		. "B4lEJGAPhOQCAACLRCQciYwk+AAAADH@SIuMJBABAACJhCSAAAAAi0QkEIlEJHzp"
		. "kwAAAEQ5tCSQAAAAfX1Ii4QkUAEAAIsUuAHajUICSJhED7YEAY1CAUhj0g+2FBFI"
		. "mCtUJGAPtgQBRYnDQQHoK0QkWEWNiAAEAABBKetFD6@LD6@ARQ+vy0G7@gUAAMHg"
		. "C0Upw0WJ2EQPr8JEAchBD6@QAdA5hCQAAQAAcg6DrCSAAAAAAQ+IlgAAAEiDxwE5"
		. "@g+O4AMAADt8JCyJvCSQAAAAD41c@@@@SIuEJEgBAACLFLgB2o1CAkiYRA+2BAGN"
		. "QgFIY9IPthQRSJgrVCRgD7YEAUWJw0EB6CtEJFhFjYgABAAAQSnrRQ+vyw+vwEUP"
		. "r8vB4AtBAcG4@gUAAEQpwA+vwg+vwkQByDmEJAABAAAPg+7+@@+DbCR8AQ+J4@7@"
		. "@4uMJPgAAADplf3@@40EkIlEJFiLRCRohcAPhY0CAACF9g+EdwEAAEyLjCRIAQAA"
		. "jUb@SIt8JAhMi5wkUAEAADH2SY1EgQRIiYQkgAAAAItEJBCJRCR8i2wkWEEDKYnz"
		. "RIsHiYwk+AAAAI1FAY1VAkhj7USJRCRgRYsDSJhIY9JIiYQkkAAAAEiLhCQQAQAA"
		. "RImEJAABAABJifgPtgQQSIuUJJAAAACJhCSYAAAASIuEJBABAAAPtgQQiYQkkAAA"
		. "AEiLhCQQAQAAD7YEKImEJJwAAADpgAAAAA8fgAAAAABBiwBBi2gEg8MCicJBiepI"
		. "ienB6hBBweoQD7bND7bSK5QkmAAAAEUPttJBic9AD7bNRInVQQ+v6olMJCgPr9I5"
		. "6n8yD7bUK5QkkAAAAESJ@UEPr+8Pr9I56n8aD7bAK4QknAAAAInKD6@RD6@AOdAP"
		. "jhgCAABJg8AIO5wkAAEAAA+Cev@@@4F8JGD@@@8Ai4wk+AAAAHcLg2wkfAEPiCD8"
		. "@@9Jg8EESIPHWEmDwwSDxhZMO4wkgAAAAA+FuP7@@0GDxAFIg7wkiAEAAAB0OYtU"
		. "JDADlCQoAQAATWPEi0QkIAOEJCABAABIi7wkiAEAAMHiEAnQRDukJJABAABCiUSH"
		. "@A+NWPP@@4t8JDAx0on4K4QkoAEAAIPAAUGJwIn4i7wkoAEAAEQPSMKNRDj@i3wk"
		. "ODn4D0@Hi3wkIInGifgrhCSYAQAAg8ABQYnBifiLvCSYAQAARA9Iyo1EOP+LfCRA"
		. "OfgPT8dBOfCJww+PUvv@@0SJ6kljwY1uAUEPr9BJY@1IY9JIAdBIA4QkQAEAAEmJ"
		. "w4nYRCnISI1wAUE52X8TSo0UHkyJ2MYAAEiDwAFIOdB19EGDwAFJAftBOeh13On@"
		. "+v@@uwMAAADpVPH@@4X2D4Tq@v@@i3wkEEyLjCQQAQAARTHbSIucJEgBAACLRCRY"
		. "QgMEm0iLnCRQAQAAQosMm41QAkhj0g+23UUPtgQRicrB6hAPttJBKdCNUAFImEEP"
		. "tgQBSGPSQQ+2FBFFD6@AKdoPttkp2EU5wnwRD6@SQTnXfAkPr8A5RCQofQmD7wEP"
		. "iG36@@9Jg8MBRDnef4bpXP7@@4NEJDAB6ZL6@@+DRCQgAemI+v@@i4wk+AAAAOk8"
		. "@v@@QcYEBgLpfO@@@4F8JGD@@@8Ai4wk+AAAAA+H9f3@@+n7@f@@QbsDAAAA6Vvw"
		. "@@+QkJCQkJCQkJCQkJCQkA=="
		MyFunc:=this.MCode(StrReplace((A_PtrSize=8?x64:x32),"@","/"))
	}
	text:=j[1], w:=j[2], h:=j[3]
		, err1:=this.Floor(j[4] ? j[5] : ini.err1)
		, err0:=this.Floor(j[4] ? j[6] : ini.err0)
		, mode:=j[7], color:=j[8], n:=j[9]
	return (!ini.bits.Scan0) ? 0 : DllCall(MyFunc.Ptr
		, "int",mode, "uint",color, "uint",n, "int",dir
		, "Ptr",ini.bits.Scan0, "int",ini.bits.Stride
		, "int",sx, "int",sy, "int",sw, "int",sh
		, "Ptr",ini.ss, "Ptr",ini.s1, "Ptr",ini.s0
		, "Ptr",text, "int",w, "int",h
		, "int",Floor(Abs(err1)*10000), "int",Floor(Abs(err0)*10000)
		, "int",(err1<0||err0<0), "Ptr",allpos_ptr, "int",ini.allpos_max
		, "int",Floor(w*ini.zoomW), "int",Floor(h*ini.zoomH))
}

code()
{
	return "
	(

//***** 机器码的 C语言 源代码 *****

int __attribute__((__stdcall__)) PicFind(
  int mode, unsigned int c, unsigned int n, int dir
			, unsigned char * Bmp, int Stride
			, int sx, int sy, int sw, int sh
			, unsigned char * ss, unsigned int * s1, unsigned int * s0
			, unsigned char * text, int w, int h
			, int err1, int err0, int more_err
			, unsigned int * allpos, int allpos_max
			, int new_w, int new_h )
{
  int ok, o, i, j, k, v, e1, e0, len1, len0, max;
  int x, y, x1, y1, x2, y2, x3, y3;
  int r, g, b, rr, gg, bb, dR, dG, dB;
  int ii, jj, RunDir, DirCount, RunCount, AllCount1, AllCount2;
  unsigned int c1, c2;
  unsigned char * ts, * gs;
  unsigned int * cors;
  ok=0; o=0; len1=0; len0=0; ts=ss+sw; gs=ss+sw*3;
  if (mode<1 || mode>5) goto Return1;
  //----------------------
  if (mode==5)
  {
    if (k=(c!=0))  // FindPic
    {
      cors=(unsigned int *)(text+w*h*4);
      r=(c>>16)&0xFF; g=(c>>8)&0xFF; b=c&0xFF; dR=r*r; dG=g*g; dB=b*b;
      for (y=0; y<h; y++)
      {
        for (x=0; x<w; x++, o+=4)
        {
          rr=text[2+o]; gg=text[1+o]; bb=text[o];
          for (i=0; i<n; i++)
          {
            c=cors[i];
            r=((c>>16)&0xFF)-rr; g=((c>>8)&0xFF)-gg; b=(c&0xFF)-bb;
            if (r*r<=dR && g*g<=dG && b*b<=dB) goto NoMatch1;
          }
          s1[len1]=(y*new_h/h)*Stride+(x*new_w/w)*4;
          s0[len1++]=(rr<<16)|(gg<<8)|bb;
          NoMatch1:;
        }
      }
    }
    else  // FindMultiColor or FindColor
    {
      cors=(unsigned int *)text;
      for (; len1<n; len1++, o+=22)
      {
        c=cors[o]; y=c>>16; x=c&0xFFFF;
        s1[len1]=(y*new_h/h)*Stride+(x*new_w/w)*4;
        s0[len1]=o+cors[o+1]*2;
      }
      cors+=2;
    }
    goto StartLookUp;
  }
  //----------------------
  // 生成查表需要的表格
  for (y=0; y<h; y++)
  {
    for (x=0; x<w; x++)
    {
      if (mode==4)
        i=(y*new_h/h)*Stride+(x*new_w/w)*4;
      else
        i=(y*new_h/h)*sw+(x*new_w/w);
      if (text[o++]=='1')
        s1[len1++]=i;
      else
        s0[len0++]=i;
    }
  }
  //----------------------
  // 颜色位置模式
  // 仅用于多色验证码的识别
  if (mode==4)
  {
    y=c>>16; x=c&0xFFFF;
    c=(y*new_h/h)*Stride+(x*new_w/w)*4;
    goto StartLookUp;
  }
  //----------------------
  // 生成二值化图像
  o=sy*Stride+sx*4; j=Stride-sw*4; i=0;
  if (mode==1)  // 颜色二值化
  {
    cors=(unsigned int *)(text+w*h); n=n*2;
    for (y=0; y<sh; y++, o+=j)
    {
      for (x=0; x<sw; x++, o+=4, i++)
      {
        rr=Bmp[2+o]; gg=Bmp[1+o]; bb=Bmp[o];
        for (k=0; k<n;)
        {
          c1=cors[k++]; c2=cors[k++];
          r=((c1>>16)&0xFF)-rr; g=((c1>>8)&0xFF)-gg; b=(c1&0xFF)-bb;
          if (c1>0xFFFFFF)
          {
            v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b<=c2) goto MatchOK1;
          }
          else
          {
            dR=(c2>>16)&0xFF; dG=(c2>>8)&0xFF; dB=c2&0xFF;
            if (r*r<=dR*dR && g*g<=dG*dG && b*b<=dB*dB) goto MatchOK1;
          }
        }
        ts[i]=0;
        continue;
        MatchOK1:
        ts[i]=1;
      }
    }
  }
  else if (mode==2)  // 灰度阈值二值化
  {
    c=(c+1)<<7;
    for (y=0; y<sh; y++, o+=j)
      for (x=0; x<sw; x++, o+=4, i++)
        ts[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15<c) ? 1:0;
  }
  else if (mode==3)  // 灰度差值二值化
  {
    for (y=0; y<sh; y++, o+=j)
    {
      for (x=0; x<sw; x++, o+=4, i++)
        gs[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15)>>7;
    }
    for (i=0, y=0; y<sh; y++)
    {
      for (x=0; x<sw; x++, i++)
      {
        if (x==0 || x==sw-1 || y==0 || y==sh-1)
          ts[i]=2;
        else
        {
          n=gs[i]+c;
          ts[i]=(gs[i-1]>n || gs[i+1]>n
			|| gs[i-sw]>n   || gs[i+sw]>n
			|| gs[i-sw-1]>n || gs[i-sw+1]>n
			|| gs[i+sw-1]>n || gs[i+sw+1]>n) ? 1:0;
        }
      }
    }
  }
  for (i=0, y=0; y<sh; y++)
  {
    for (x=0; x<sw; x++, i++)
    {
      r=ts[i];
      g=(x==0) ? 3 : ts[i-1];
      b=(x==sw-1) ? 3 : ts[i+1];
      if (more_err)
        ss[i]=(r==2||r==1||g==1||b==1)<<1|(r==2||r==0||g==0||b==0);
      else
        ss[i]=(r==2||r==1)<<1|(r==2||r==0);
    }
  }
  //----------------------
  StartLookUp:
  err1=len1*err1/10000;
  err0=len0*err0/10000;
  if (err1>=len1) len1=0;
  if (err0>=len0) len0=0;
  max=(len1>len0) ? len1 : len0;
  w=new_w; h=new_h;
  x1=0; y1=0; x2=sw-w; y2=sh-h;
  if (mode>3)
  {
    for (i=0, j=sw*sh; i<j; i++)
      ss[i]=1;
  }
  else
  {
    if (more_err) sx++;
  }
  // 1 ==> ( Left to Right ) Top to Bottom
  // 2 ==> ( Right to Left ) Top to Bottom
  // 3 ==> ( Left to Right ) Bottom to Top
  // 4 ==> ( Right to Left ) Bottom to Top
  // 5 ==> ( Top to Bottom ) Left to Right
  // 6 ==> ( Bottom to Top ) Left to Right
  // 7 ==> ( Top to Bottom ) Right to Left
  // 8 ==> ( Bottom to Top ) Right to Left
  // 9 ==> Center to Four Sides
  if (dir==9)
  {
    x=(x1+x2)/2; y=(y1+y2)/2; i=x2-x1+1; j=y2-y1+1;
    AllCount1=i*j; i=(i>j) ? i+8 : j+8;
    AllCount2=i*i; RunCount=0; DirCount=1; RunDir=0;
    for (ii=0; RunCount<AllCount1 && ii<AllCount2; ii++)
    {
      for(jj=0; jj<DirCount; jj++)
      {
        if(x>=x1 && x<=x2 && y>=y1 && y<=y2)
        {
          RunCount++;
          goto FindPos;
          FindPos_GoBak:;
        }
        if (RunDir==0) y--;
        else if (RunDir==1) x++;
        else if (RunDir==2) y++;
        else if (RunDir==3) x--;
      }
      if (RunDir & 1) DirCount++;
      RunDir = (++RunDir) & 3;
    }
    goto Return1;
  }
  if (dir<1 || dir>8) dir=1;
  if (--dir>3) { r=y1; y1=x1; x1=r; r=y2; y2=x2; x2=r; }
  for (y3=y1; y3<=y2; y3++)
  {
    for (x3=x1; x3<=x2; x3++)
    {
      y=(dir & 2) ? y1+y2-y3 : y3;
      x=(dir & 1) ? x1+x2-x3 : x3;
      if (dir>3) { r=y; y=x; x=r; }
      //----------------------
      FindPos:
      e1=err1; e0=err0; o=y*sw+x;
      if (ss[o]==0) goto NoMatch;
      if (mode<4)
      {
        for (i=0; i<max; i++)
        {
          if (i<len1 && ss[o+s1[i]]<2 && (--e1)<0) goto NoMatch;
          if (i<len0 && (ss[o+s0[i]]&1)==0 && (--e0)<0) goto NoMatch;
        }
      }
      else if (mode==5)
      {
        o=(sy+y)*Stride+(sx+x)*4;
        if (k)
        {
          for (i=0; i<max; i++)
          {
            j=o+s1[i]; c=s0[i]; r=Bmp[2+j]-((c>>16)&0xFF);
            g=Bmp[1+j]-((c>>8)&0xFF); b=Bmp[j]-(c&0xFF);
            if ((r*r>dR || g*g>dG || b*b>dB) && (--e1)<0) goto NoMatch;
          }
        }
        else
        {
          for (i=0; i<max; i++)
          {
            j=o+s1[i]; rr=Bmp[2+j]; gg=Bmp[1+j]; bb=Bmp[j];
            for (j=i*22, v=cors[j]>0xFFFFFF, n=s0[i]; j<n;)
            {
              c1=cors[j++]; c2=cors[j++];
              r=((c1>>16)&0xFF)-rr; g=((c1>>8)&0xFF)-gg; b=(c1&0xFF)-bb;
              dR=(c2>>16)&0xFF; dG=(c2>>8)&0xFF; dB=c2&0xFF;
              if (r*r<=dR*dR && g*g<=dG*dG && b*b<=dB*dB)
              {
                if (v) goto NoMatch2;
                goto MatchOK;
              }
            }
            if (v) continue;
            NoMatch2:
            if ((--e1)<0) goto NoMatch;
            MatchOK:;
          }
        }
      }
      else  // mode==4
      {
        o=(sy+y)*Stride+(sx+x)*4;
        j=o+c; rr=Bmp[2+j]; gg=Bmp[1+j]; bb=Bmp[j];
        for (i=0; i<max; i++)
        {
          if (i<len1)
          {
            j=o+s1[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b>n && (--e1)<0) goto NoMatch;
          }
          if (i<len0)
          {
            j=o+s0[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b<=n && (--e0)<0) goto NoMatch;
          }
        }
      }
      ok++;
      if (allpos!=0)
      {
        allpos[ok-1]=(sy+y)<<16|(sx+x);
        if (ok>=allpos_max) goto Return1;
      }
      // 跳过可能会重叠的区域
      r=y-h+1; r=(r>=0) ? r:0; rr=y+h-1; rr=(rr<=sh-h) ? rr:sh-h;
      g=x-w+1; g=(g>=0) ? g:0; gg=x+w-1; gg=(gg<=sw-w) ? gg:sw-w;
      for (i=r; i<=rr; i++)
      {
        for (j=g; j<=gg; j++)
          ss[i*sw+j]=0;
      }
      NoMatch:
      if (dir==9) goto FindPos_GoBak;
    }
  }
  //----------------------
  Return1:
  return ok;
}

	)"
}

PicInfo(text)
{
	local
	if !InStr(text, "$")
		return
	static init, info, bmp
	if !VarSetCapacity(init) && (init:="1")
		info:=[], bmp:=[]
	key:=(r:=StrLen(v:=Trim(text,"|")))<10000 ? v
: DllCall("ntdll\RtlComputeCrc32", "uint",0
		, "Ptr",&v, "uint",r*(1+!!A_IsUnicode), "uint")
	if info.HasKey(key)
		return info[key]
	comment:="", seterr:=err1:=err0:=0
	; You Can Add Comment Text within The <>
	if RegExMatch(v, "O)<([^>\n]*)>", r)
		v:=StrReplace(v,r[0]), comment:=Trim(r[1])
	; You can Add two fault-tolerant in the [], separated by commas
	if RegExMatch(v, "O)\[([^\]\n]*)]", r)
	{
		v:=StrReplace(v,r[0]), r:=StrSplit(r[1] ",", ",")
			, seterr:=1, err1:=r[1], err0:=r[2]
	}
	color:=SubStr(v,1,InStr(v,"$")-1), v:=Trim(SubStr(v,InStr(v,"$")+1))
	mode:=InStr(color,"##") ? 5 : InStr(color,"#") ? 4
: InStr(color,"**") ? 3 : InStr(color,"*") ? 2 : 1
	color:=RegExReplace(color, "[*#\s]")
	(mode=1 || mode=5) && color:=StrReplace(color,"0x")
  if (mode=5)
  {
    if !(v~="/[\s\-\w]+/[\s\-\w,/]+$")
    {
		; <FindPic> : Text 参数需要手动输入
		; Text:='|<>##DRDGDB-RRGGBB1-RRGGBB2... $ d:\a.bmp'
		; 0xRRGGBB1(+/-0xDRDGDB)... 都是透明色,不参与匹配
      if !(hBM:=LoadPicture(v))
        return
      this.GetBitmapWH(hBM, w, h)
      if (w<1 || h<1)
        return
      hBM2:=this.CreateDIBSection(w, h, 32, Scan0)
      this.CopyHBM(hBM2, 0, 0, hBM, 0, 0, w, h)
      DllCall("DeleteObject", "Ptr",hBM)
      if (!Scan0)
        return
		; 所有用于搜索的图片都缓存了
      StrReplace(color, "-",, n)
      bmp.Push(buf:=this.Buffer(w*h*4 + n*4)), v:=buf.Ptr, p:=v+w*h*4-4
      DllCall("RtlMoveMemory", "Ptr",v, "Ptr",Scan0, "Ptr",w*h*4)
      DllCall("DeleteObject", "Ptr",hBM2)
      For k1,v1 in StrSplit(color, "-")
      if (k1>1)
        NumPut(this.ToRGB(v1), 0|p+=4, "uint")
      color:=this.Floor("0x" StrSplit(color "-", "-")[1])|0x1000000
    }
    else
    {
		; <FindMultiColor> 或 <FindColor> : 找色 是仅有一个点的 找多色
		; Text:='|<>##DRDGDB $ 0/0/RRGGBB1-DRDGDB1/RRGGBB2, xn/yn/-RRGGBB3/RRGGBB4, ...'
		; '##'之后的颜色 (0xDRDGDB) 是所有颜色的默认偏色(各个分量允许的变化值)
		; 初始点 (0,0) 匹配 0xRRGGBB1(+/-0xDRDGDB1) 或者 0xRRGGBB2(+/-0xDRDGDB),
		; 点 (xn,yn) 匹配 排除 0xRRGGBB3(+/-0xDRDGDB) 和排除 0xRRGGBB4(+/-0xDRDGDB)
		; 点坐标后面以 '-' 开头表示要排除后面的所有颜色,其他颜色都匹配
		; 每个点最多允许匹配10组颜色 (xn/yn/RRGGBB1/.../RRGGBB10)
      arr:=StrSplit(Trim(RegExReplace(v, "i)\s|0x"), ","), ",")
      if !(n:=arr.Length())
        return
      bmp.Push(buf:=this.Buffer(n*22*4)), v:=buf.Ptr
			, color:=StrSplit(color "-", "-")[1]
      For k1,v1 in arr
      {
        r:=StrSplit(v1 "/", "/")
			, x:=this.Floor(r[1]), y:=this.Floor(r[2])
			, (A_Index=1) ? (x1:=x2:=x, y1:=y2:=y)
: (x1:=Min(x1,x), x2:=Max(x2,x), y1:=Min(y1,y), y2:=Max(y2,y))
      }
      For k1,v1 in arr
      {
        r:=StrSplit(v1 "/", "/")
		, x:=this.Floor(r[1])-x1, y:=this.Floor(r[2])-y1
		, n1:=Min(Max(r.Length()-3, 0), 10)
		, NumPut(y<<16|x, 0|p:=v+(A_Index-1)*22*4, "uint")
		, NumPut(n1, 0|p+=4, "uint")
        Loop % n1
          k1:=(InStr(v1:=r[2+A_Index], "-")=1 ? 0x1000000:0)
		, c:=StrSplit(Trim(v1,"-") "-" color, "-")
		, NumPut(this.ToRGB(c[1])&0xFFFFFF|k1, 0|p+=4, "uint")
		, NumPut(this.Floor("0x" c[2]), 0|p+=4, "uint")
      }
      color:=0, w:=x2-x1+1, h:=y2-y1+1
    }
  }
  else
  {
    r:=StrSplit(v ".", "."), w:=this.Floor(r[1])
		, v:=this.base64tobit(r[2]), h:=StrLen(v)//w
    if (w<1 || h<1 || StrLen(v)!=w*h)
      return
    arr:=StrSplit(Trim(color, "/"), "/")
    if !(n:=arr.Length())
      return
    bmp.Push(buf:=this.Buffer(StrPut(v, "CP0") + n*2*4))
		, StrPut(v, buf.Ptr, "CP0"), v:=buf.Ptr, p:=v+w*h-4
		, color:=this.Floor(arr[1])
    if (mode=1)
    {
      For k1,v1 in arr
      {
        k1:=(InStr(v1, "@") ? 0x1000000:0)
		, r:=StrSplit(v1 "@1", "@"), x:=this.Floor(r[2])
		, x:=(x<=0||x>1?1:x), x:=Floor(4606*255*255*(1-x)*(1-x))
		, c:=StrSplit(Trim(r[1],"-") "-" Format("{:X}",x), "-")
		, NumPut(this.ToRGB(c[1])&0xFFFFFF|k1, 0|p+=4, "uint")
		, NumPut(this.Floor("0x" c[2]), 0|p+=4, "uint")
      }
    }
    else if (mode=4)
    {
      r:=StrSplit(arr[1] "@1", "@"), n:=this.Floor(r[2])
		, n:=(n<=0||n>1?1:n), n:=Floor(4606*255*255*(1-n)*(1-n))
		, c:=this.Floor(r[1]), color:=((c-1)//w)<<16|Mod(c-1,w)
    }
  }
  return info[key]:=[v, w, h, seterr, err1, err0, mode, color, n, comment]
}

ToRGB(color)  ; color 可以使用: RRGGBB, Red, Yellow, Black, White
{
  static init, tab
  if !VarSetCapacity(init) && (init:="1")
    tab:=Object("Black", "000000", "White", "FFFFFF"
		, "Red", "FF0000", "Green", "008000", "Blue", "0000FF"
		, "Yellow", "FFFF00", "Silver", "C0C0C0", "Gray", "808080"
		, "Teal", "008080", "Navy", "000080", "Aqua", "00FFFF"
		, "Olive", "808000", "Lime", "00FF00", "Fuchsia", "FF00FF"
		, "Purple", "800080", "Maroon", "800000")
  return this.Floor("0x" (tab.HasKey(color)?tab[color]:color))
}

Buffer(size, FillByte:="")
{
  local
  buf:={}, buf.SetCapacity("_key", size), p:=buf.GetAddress("_key")
		, (FillByte!="" && DllCall("RtlFillMemory","Ptr",p,"Ptr",size,"uchar",FillByte))
		, buf.Ptr:=p, buf.Size:=size
  return buf
}

GetBitsFromScreen(ByRef x:=0, ByRef y:=0, ByRef w:=0, ByRef h:=0
		, ScreenShot:=1, ByRef zx:=0, ByRef zy:=0, ByRef zw:=0, ByRef zh:=0)
{
  local
  static init, CAPTUREBLT
  if !VarSetCapacity(init) && (init:="1")  ; thanks Descolada
  {
    DllCall("Dwmapi\DwmIsCompositionEnabled", "Int*",i:=0)
    CAPTUREBLT:=i ? 0 : 0x40000000
  }
  (!IsObject(this.bits) && this.bits:={Scan0:0, hBM:0, oldzw:0, oldzh:0})
		, bits:=this.bits
  if (!ScreenShot && bits.Scan0)
  {
    zx:=bits.zx, zy:=bits.zy, zw:=bits.zw, zh:=bits.zh
		, w:=Min(x+w,zx+zw), x:=Max(x,zx), w-=x
		, h:=Min(y+h,zy+zh), y:=Max(y,zy), h-=y
    return bits
  }
  bch:=A_BatchLines, cri:=A_IsCritical
  Critical
  bits.BindWindow:=id:=this.BindWindow(0,0,1)
  if (id)
  {
    WinGet, id, ID, ahk_id %id%
    WinGetPos, zx, zy, zw, zh, ahk_id %id%
  }
  if (!id)
  {
    SysGet, zx, 76
    SysGet, zy, 77
    SysGet, zw, 78
    SysGet, zh, 79
  }
  this.UpdateBits(bits, zx, zy, zw, zh)
		, w:=Min(x+w,zx+zw), x:=Max(x,zx), w-=x
		, h:=Min(y+h,zy+zh), y:=Max(y,zy), h-=y
  if (!ScreenShot || w<1 || h<1 || !bits.hBM)
  {
    Critical % cri
    SetBatchLines % bch
    return bits
  }
  if IsFunc(k:="GetBitsFromScreen2")
		&& %k%(bits, x-zx, y-zy, w, h)
  {
	; Get the bind window use bits.BindWindow
	; Each small range of data obtained from DXGI must be
	; copied to the screenshot cache using FindText().CopyBits()
    zx:=bits.zx, zy:=bits.zy, zw:=bits.zw, zh:=bits.zh
    Critical % cri
    SetBatchLines % bch
    return bits
  }
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",bits.hBM, "Ptr")
  if (id)
  {
    if (mode:=this.BindWindow(0,0,0,1))<2
    {
      hDC:=DllCall("GetDCEx", "Ptr",id, "Ptr",0, "int",3, "Ptr")
      DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",hDC, "int",x-zx, "int",y-zy, "uint",0xCC0020|CAPTUREBLT)
      DllCall("ReleaseDC", "Ptr",id, "Ptr",hDC)
    }
    else
    {
      hBM2:=this.CreateDIBSection(zw, zh)
      mDC2:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
      oBM2:=DllCall("SelectObject", "Ptr",mDC2, "Ptr",hBM2, "Ptr")
      DllCall("PrintWindow", "Ptr",id, "Ptr",mDC2, "uint",(mode>3)*3)
      DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",mDC2, "int",x-zx, "int",y-zy, "uint",0xCC0020)
      DllCall("SelectObject", "Ptr",mDC2, "Ptr",oBM2)
      DllCall("DeleteDC", "Ptr",mDC2)
      DllCall("DeleteObject", "Ptr",hBM2)
    }
  }
  else
  {
    hDC:=DllCall("GetWindowDC","Ptr",id:=DllCall("GetDesktopWindow","Ptr"),"Ptr")
    DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",hDC, "int",x, "int",y, "uint",0xCC0020|CAPTUREBLT)
    DllCall("ReleaseDC", "Ptr",id, "Ptr",hDC)
  }
  if this.CaptureCursor(0,0,0,0,0,1)
    this.CaptureCursor(mDC, zx, zy, zw, zh)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteDC", "Ptr",mDC)
  Critical % cri
  SetBatchLines % bch
  return bits
}

UpdateBits(bits, zx, zy, zw, zh)
{
  local
  if (zw>bits.oldzw || zh>bits.oldzh || !bits.hBM)
  {
    Try DllCall("DeleteObject", "Ptr",bits.hBM)
    bits.hBM:=this.CreateDIBSection(zw, zh, bpp:=32, ppvBits)
		, bits.Scan0:=(!bits.hBM ? 0:ppvBits)
		, bits.Stride:=((zw*bpp+31)//32)*4
		, bits.oldzw:=zw, bits.oldzh:=zh
  }
  bits.zx:=zx, bits.zy:=zy, bits.zw:=zw, bits.zh:=zh
}

CreateDIBSection(w, h, bpp:=32, ByRef ppvBits:=0)
{
  local
  VarSetCapacity(bi, 40, 0), NumPut(40, bi, 0, "int")
		, NumPut(w, bi, 4, "int"), NumPut(-h, bi, 8, "int")
		, NumPut(1, bi, 12, "short"), NumPut(bpp, bi, 14, "short")
  return DllCall("CreateDIBSection", "Ptr",0, "Ptr",&bi
		, "int",0, "Ptr*",ppvBits:=0, "Ptr",0, "int",0, "Ptr")
}

GetBitmapWH(hBM, ByRef w, ByRef h)
{
  local
  VarSetCapacity(bm, size:=(A_PtrSize=8 ? 32:24))
		, DllCall("GetObject", "Ptr",hBM, "int",size, "Ptr",&bm)
		, w:=NumGet(bm,4,"int"), h:=Abs(NumGet(bm,8,"int"))
}

CopyHBM(hBM1, x1, y1, hBM2, x2, y2, w, h, Clear:=0)
{
  local
  if (w<1 || h<1 || !hBM1 || !hBM2)
    return
  mDC1:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM1:=DllCall("SelectObject", "Ptr",mDC1, "Ptr",hBM1, "Ptr")
  mDC2:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM2:=DllCall("SelectObject", "Ptr",mDC2, "Ptr",hBM2, "Ptr")
  DllCall("BitBlt", "Ptr",mDC1, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC2, "int",x2, "int",y2, "uint",0xCC0020)
  if (Clear)
    DllCall("BitBlt", "Ptr",mDC1, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC1, "int",x1, "int",y1, "uint",MERGECOPY:=0xC000CA)
  DllCall("SelectObject", "Ptr",mDC1, "Ptr",oBM1)
  DllCall("DeleteDC", "Ptr",mDC1)
  DllCall("SelectObject", "Ptr",mDC2, "Ptr",oBM2)
  DllCall("DeleteDC", "Ptr",mDC2)
}

CopyBits(Scan01,Stride1,x1,y1,Scan02,Stride2,x2,y2,w,h,Reverse:=0)
{
  local
  if (w<1 || h<1 || !Scan01 || !Scan02)
    return
  static init, MFCopyImage
  if !VarSetCapacity(init) && (init:="1")
  {
    MFCopyImage:=DllCall("GetProcAddress", "Ptr"
		, DllCall("LoadLibrary", "Str","Mfplat.dll", "Ptr")
		, "AStr","MFCopyImage", "Ptr")
  }
  if (MFCopyImage && !Reverse)  ; thanks QQ:任性
  {
    return DllCall(MFCopyImage
		, "Ptr",Scan01+y1*Stride1+x1*4, "int",Stride1
		, "Ptr",Scan02+y2*Stride2+x2*4, "int",Stride2
		, "uint",w*4, "uint",h)
  }
  ListLines % (lls:=A_ListLines)?0:0
  SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
  p1:=Scan01+(y1-1)*Stride1+x1*4
		, p2:=Scan02+(y2-1)*Stride2+x2*4, w*=4
  if (Reverse)
    p2+=(h+1)*Stride2, Stride2:=-Stride2
  Loop % h
    DllCall("RtlMoveMemory","Ptr",p1+=Stride1,"Ptr",p2+=Stride2,"Ptr",w)
  SetBatchLines % bch
  ListLines % lls
}

DrawHBM(hBM, lines)
{
  local
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM, "Ptr")
  oldc:="", brush:=0, VarSetCapacity(rect, 16)
  For k,v in lines  ; [ [x, y, w, h, color] ]
  if IsObject(v)
  {
    if (oldc!=v[5])
    {
      oldc:=v[5], BGR:=(oldc&0xFF)<<16|oldc&0xFF00|(oldc>>16)&0xFF
      DllCall("DeleteObject", "Ptr",brush)
      brush:=DllCall("CreateSolidBrush", "uint",BGR, "Ptr")
    }
    DllCall("SetRect", "Ptr",&rect, "int",v[1], "int",v[2]
		, "int",v[1]+v[3], "int",v[2]+v[4])
    DllCall("FillRect", "Ptr",mDC, "Ptr",&rect, "Ptr",brush)
  }
  DllCall("DeleteObject", "Ptr",brush)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteObject", "Ptr",mDC)
}

	; 绑定窗口从而可以后台查找这个窗口的图像
	; 相当于始终在前台。解绑窗口使用 FindText().BindWindow(0)

BindWindow(bind_id:=0, bind_mode:=0, get_id:=0, get_mode:=0)
{
  local
  (!IsObject(this.bind) && this.bind:={id:0, mode:0, oldStyle:0})
		, bind:=this.bind
  if (get_id)
    return bind.id
  if (get_mode)
    return bind.mode
  if (bind_id)
  {
    bind.id:=bind_id:=this.Floor(bind_id)
		, bind.mode:=bind_mode, bind.oldStyle:=0
    if (bind_mode & 1)
    {
      WinGet, i, ExStyle, ahk_id %bind_id%
      bind.oldStyle:=i
      WinSet, Transparent, 255, ahk_id %bind_id%
      Loop 30
      {
        Sleep 100
        WinGet, i, Transparent, ahk_id %bind_id%
      }
      Until (i=255)
    }
  }
  else
  {
    bind_id:=bind.id
    if (bind.mode & 1)
      WinSet, ExStyle, % bind.oldStyle, ahk_id %bind_id%
    bind.id:=0, bind.mode:=0, bind.oldStyle:=0
  }
}

	; 使用 FindText().CaptureCursor(1) 设置抓图时捕获鼠标
	; 使用 FindText().CaptureCursor(0) 取消抓图时捕获鼠标

CaptureCursor(hDC:=0, zx:=0, zy:=0, zw:=0, zh:=0, get_cursor:=0)
{
  local
  if (get_cursor)
    return this.Cursor
  if (hDC=1 || hDC=0) && (zw=0)
  {
    this.Cursor:=hDC
    return
  }
  VarSetCapacity(mi, 40, 0), NumPut(16+A_PtrSize, mi, "int")
  DllCall("GetCursorInfo", "Ptr",&mi)
  bShow:=NumGet(mi, 4, "int")
  hCursor:=NumGet(mi, 8, "Ptr")
  x:=NumGet(mi, 8+A_PtrSize, "int")
  y:=NumGet(mi, 12+A_PtrSize, "int")
  if (!bShow) || (x<zx || y<zy || x>=zx+zw || y>=zy+zh)
    return
  VarSetCapacity(ni, 40, 0)
  DllCall("GetIconInfo", "Ptr",hCursor, "Ptr",&ni)
  xCenter:=NumGet(ni, 4, "int")
  yCenter:=NumGet(ni, 8, "int")
  hBMMask:=NumGet(ni, (A_PtrSize=8?16:12), "Ptr")
  hBMColor:=NumGet(ni, (A_PtrSize=8?24:16), "Ptr")
  DllCall("DrawIconEx", "Ptr",hDC
		, "int",x-xCenter-zx, "int",y-yCenter-zy, "Ptr",hCursor
		, "int",0, "int",0, "int",0, "int",0, "int",3)
  DllCall("DeleteObject", "Ptr",hBMMask)
  DllCall("DeleteObject", "Ptr",hBMColor)
}

MCode(hex)
{
  local
  flag:=((hex~="[^\s\da-fA-F]")?1:4), hex:=RegExReplace(hex, "[\s=]")
  code:=this.Buffer(len:=(flag=1 ? StrLen(hex)//4*3+3 : StrLen(hex)//2))
  DllCall("crypt32\CryptStringToBinary", "Str",hex, "uint",0
		, "uint",flag, "Ptr",code.Ptr, "uint*",len, "Ptr",0, "Ptr",0)
  DllCall("VirtualProtect", "Ptr",code.Ptr, "Ptr",len, "uint",0x40, "Ptr*",0)
  return code
}

bin2hex(addr, size, base64:=1)
{
  local
  flag:=(base64 ? 1:4)|0x40000000, len:=0
  Loop 2
    DllCall("Crypt32\CryptBinaryToString", "Ptr",addr, "uint",size
		, "uint",flag, "Ptr",(A_Index=1?0:(p:=this.Buffer(len*2)).Ptr), "uint*",len)
  return RegExReplace(StrGet(p.Ptr, len), "\s+")
}

base64tobit(s)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  SetFormat, IntegerFast, d
  Loop Parse, Chars
    if InStr(s, A_LoopField, 1)
      s:=RegExReplace(s, "[" A_LoopField "]", ((i:=A_Index-1)>>5&1)
      . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1))
  s:=RegExReplace(RegExReplace(s,"[^01]+"),"10*$")
  ListLines % lls
  return s
}

bit2base64(s)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  s:=RegExReplace(s,"[^01]+")
  s.=SubStr("100000",1,6-Mod(StrLen(s),6))
  s:=RegExReplace(s,".{6}","|$0")
  Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  SetFormat, IntegerFast, d
  Loop Parse, Chars
    s:=StrReplace(s, "|" . ((i:=A_Index-1)>>5&1)
    . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1), A_LoopField)
  ListLines % lls
  return s
}

ASCII(s)
{
  local
  if RegExMatch(s, "O)\$(\d+)\.([\w+/]+)", r)
  {
    s:=RegExReplace(this.base64tobit(r[2]),".{" r[1] "}","$0`n")
    s:=StrReplace(StrReplace(s,"0","_"),"1","0")
  }
  else s:=""
  return s
}

	; 可以在脚本的开头用 FindText().PicLib(Text,1) 导入字库,
	; 然后使用 FindText().PicLib("说明文字1|说明文字2|...") 获取字库中的数据

PicLib(comments, add_to_Lib:=0, index:=1)
{
  local
  (!IsObject(this.Lib) && this.Lib:=[]), Lib:=this.Lib
		, (!Lib.HasKey(index) && Lib[index]:=[]), Lib:=Lib[index]
  if (add_to_Lib)
  {
    re:="O)<([^>\n]*)>[^$\n]+\$[^""\r\n]+"
    Loop Parse, comments, |
      if RegExMatch(A_LoopField, re, r)
      {
        s1:=Trim(r[1]), s2:=""
        Loop Parse, s1
          s2.=Format("_{:d}", Ord(A_LoopField))
        Lib[s2]:=r[0]
      }
    Lib[""]:=""
  }
  else
  {
    Text:=""
    Loop Parse, comments, |
    {
      s1:=Trim(A_LoopField), s2:=""
      Loop Parse, s1
        s2.=Format("_{:d}", Ord(A_LoopField))
      if Lib.HasKey(s2)
        Text.="|" . Lib[s2]
    }
    return Text
  }
}

	; 分割字符串为单个文字并获取数据

PicN(Number, index:=1)
{
  return this.PicLib(RegExReplace(Number,".","|$0"), 0, index)
}

	; 使用 FindText().PicX(Text) 可以将文字分割成多个单字的组合,从而适应间隔变化
	; 但是不能用于“颜色位置二值化”模式, 因为位置是与整体图像相关的

PicX(Text)
{
  local
  if !RegExMatch(Text, "O)(<[^$\n]+)\$(\d+)\.([\w+/]+)", r)
    return Text
  v:=this.base64tobit(r[3]), Text:=""
  c:=StrLen(StrReplace(v,"0"))<=StrLen(v)//2 ? "1":"0"
  txt:=RegExReplace(v,".{" r[2] "}","$0`n")
  While InStr(txt,c)
  {
    While !(txt~="m`n)^" c)
      txt:=RegExReplace(txt,"m`n)^.")
    i:=0
    While (txt~="m`n)^.{" i "}" c)
      i:=Format("{:d}",i+1)
    v:=RegExReplace(txt,"m`n)^(.{" i "}).*","$1")
    txt:=RegExReplace(txt,"m`n)^.{" i "}")
    if (v!="")
      Text.="|" r[1] "$" i "." this.bit2base64(v)
  }
  return Text
}

	; 截屏,作为后续操作要用的“上一次的截屏”

ScreenShot(x1:=0, y1:=0, x2:=0, y2:=0)
{
  this.FindText(,, x1, y1, x2, y2)
}

	; 从“上一次的截屏”中快速获取指定坐标的RGB颜色
	; 如果坐标超出了屏幕范围,将返回白色

GetColor(x, y, fmt:=1)
{
  local
  bits:=this.GetBitsFromScreen(,,,,0,zx,zy,zw,zh), x-=zx, y-=zy
		, c:=(x>=0 && x<zw && y>=0 && y<zh && bits.Scan0)
  ? NumGet(bits.Scan0+y*bits.Stride+x*4,"uint") : 0xFFFFFF
  return (fmt ? Format("0x{:06X}",c&0xFFFFFF) : c)
}

	; 在“上一次的截屏”中设置点的RGB颜色

SetColor(x, y, color:=0x000000)
{
  local
  bits:=this.GetBitsFromScreen(,,,,0,zx,zy,zw,zh), x-=zx, y-=zy
  if (x>=0 && x<zw && y>=0 && y<zh && bits.Scan0)
    NumPut(color, bits.Scan0+y*bits.Stride+x*4, "uint")
}

	; 根据 FindText() 的结果识别一行文字或验证码
	; offsetX 为两个文字的最大间隔,超过会插入*号
	; offsetY 为两个文字的最大高度差
	; overlapW 用于设置覆盖的宽度
	; 最后返回数组:{text:识别结果, x:结果左上角X, y:结果左上角Y, w:宽, h:高}

Ocr(ok, offsetX:=20, offsetY:=20, overlapW:=0)
{
  local
  ocr_Text:=ocr_X:=ocr_Y:=min_X:=dx:=""
  For k,v in ok
    x:=v.1
		, min_X:=(A_Index=1 || x<min_X ? x : min_X)
		, max_X:=(A_Index=1 || x>max_X ? x : max_X)
  While (min_X!="" && min_X<=max_X)
  {
    LeftX:=""
    For k,v in ok
    {
      x:=v.1, y:=v.2
      if (x<min_X) || (ocr_Y!="" && Abs(y-ocr_Y)>offsetY)
        Continue
	; Get the leftmost X coordinates
      if (LeftX="" || x<LeftX)
        LeftX:=x, LeftY:=y, LeftW:=v.3, LeftH:=v.4, LeftOCR:=v.id
    }
    if (LeftX="")
      Break
    if (ocr_X="")
      ocr_X:=LeftX, min_Y:=LeftY, max_Y:=LeftY+LeftH
	; If the interval exceeds the set value, add "*" to the result
    ocr_Text.=(ocr_Text!="" && LeftX>dx ? "*":"") . LeftOCR
	; Update for next search
    min_X:=LeftX+LeftW-(overlapW>LeftW//2 ? LeftW//2:overlapW)
		, dx:=LeftX+LeftW+offsetX, ocr_Y:=LeftY
		, (LeftY<min_Y && min_Y:=LeftY)
		, (LeftY+LeftH>max_Y && max_Y:=LeftY+LeftH)
  }
  if (ocr_X="")
    ocr_X:=0, min_Y:=0, min_X:=0, max_Y:=0
  return {text:ocr_Text, x:ocr_X, y:min_Y
		, w: min_X-ocr_X, h: max_Y-min_Y}
}

	; 按照从左到右、从上到下的顺序排序FindText()的结果
	; 忽略轻微的Y坐标差距,返回排序后的数组对象

Sort(ok, dy:=10)
{
  local
  if !IsObject(ok)
    return ok
  s:="", n:=150000, ypos:=[]
  For k,v in ok
  {
    x:=v.x, y:=v.y, add:=1
    For k1,v1 in ypos
    if Abs(y-v1)<=dy
    {
      y:=v1, add:=0
      Break
    }
    if (add)
      ypos.Push(y)
    s.=(y*n+x) "." k "|"
  }
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 以指定点为中心,按从近到远排序FindText()的结果,返回排序后的数组

Sort2(ok, px, py)
{
  local
  if !IsObject(ok)
    return ok
  s:=""
  For k,v in ok
    s.=((v.x-px)**2+(v.y-py)**2) "." k "|"
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 按指定的查找方向,排序FindText()的结果,返回排序后的数组

Sort3(ok, dir:=1)
{
  local
  if !IsObject(ok)
    return ok
  s:="", n:=150000
  For k,v in ok
    x:=v.1, y:=v.2
		, s.=(dir=1 ? y*n+x
: dir=2 ? y*n-x
: dir=3 ? -y*n+x
: dir=4 ? -y*n-x
: dir=5 ? x*n+y
: dir=6 ? x*n-y
: dir=7 ? -x*n+y
: dir=8 ? -x*n-y : y*n+x) "." k "|"
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 提示某个坐标的位置,或远程控制中当前鼠标的位置

MouseTip(x:="", y:="", w:=10, h:=10, d:=3)
{
  local
  if (x="")
  {
    VarSetCapacity(pt,16,0), DllCall("GetCursorPos","Ptr",&pt)
    x:=NumGet(pt,0,"uint"), y:=NumGet(pt,4,"uint")
  }
  Loop 4
  {
    this.RangeTip(x-w, y-h, 2*w+1, 2*h+1, (A_Index & 1 ? "Red":"Blue"), d)
    Sleep 500
  }
  this.RangeTip()
}

	; 显示范围的边框,类似于 ToolTip

RangeTip(x:="", y:="", w:="", h:="", color:="Red", d:=3)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  static init, Range
  if !VarSetCapacity(init) && (init:="1")
    Range:=[0,0,0,0]
  if (x="")
  {
    Loop 4
      if (Range[i:=A_Index])
        Range[i].Destroy(), Range[i]:=""
    ListLines % lls
    return
  }
  if (!Range[1])
  {
    Loop 4
      Range[A_Index]:=Gui("+AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
  }
  x:=this.Floor(x), y:=this.Floor(y), w:=this.Floor(w), h:=this.Floor(h)
		, d:=this.Floor(d)
  Loop 4
  {
    i:=A_Index
		, x1:=(i=2 ? x+w : x-d)
		, y1:=(i=3 ? y+h : y-d)
		, w1:=(i=1 || i=3 ? w+2*d : d)
		, h1:=(i=2 || i=4 ? h+2*d : d)
    Range[i].BackColor:=color
    Range[i].Show("NA x" x1 " y" y1 " w" w1 " h" h1)
  }
  ListLines % lls
}

State(key)
{
  return GetKeyState(key,"P") || GetKeyState(key)
}

	; 用鼠标左右键选取屏幕范围

GetRange(ww:=25, hh:=8, key:="RButton")
{
  local
  static init, KeyOff, hk
  if !VarSetCapacity(init) && (init:="1")
    KeyOff:=this.GetRange.Bind(this, "Off")
  if (ww=="Off")
    return hk:=Trim(A_ThisHotkey, "*")
	;---------------------
  GetRange_HotkeyIf:=_Gui:=Gui()
  _Gui.Opt("-Caption +ToolWindow +E0x80000")
  _Gui.Title:="GetRange_HotkeyIf"
  _Gui.Show("NA x0 y0 w0 h0")
	;---------------------
  if GetKeyState("Ctrl")
    Send {Ctrl Up}
  Hotkey, IfWinExist, GetRange_HotkeyIf
  keys:=key "|Up|Down|Left|Right"
  For k,v in StrSplit(keys, "|")
  {
    if GetKeyState(v)
      Send {%v% Up}
    Hotkey, *%v%, %KeyOff%, On UseErrorLevel
  }
  Hotkey, IfWinExist
	;---------------------
  Critical % (cri:=A_IsCritical)?"Off":"Off"
  CoordMode, Mouse
  tip:=this.Lang("s5")
  hk:="", oldx:=oldy:="", keydown:=0
  Loop
  {
    Sleep 50
    MouseGetPos, x2, y2
    if (hk=key) || this.State(key) || this.State("Ctrl")
    {
      keydown++
      if (keydown=1)
        MouseGetPos, x1, y1, Bind_ID
      timeout:=A_TickCount+3000
      While (A_TickCount<timeout) && (this.State(key) || this.State("Ctrl"))
        Sleep 50
      hk:=""
      if (keydown>=2)
        Break
    }
    else if (hk="Up") || this.State("Up")
      (hh>1 && hh--), hk:=""
    else if (hk="Down") || this.State("Down")
      hh++, hk:=""
    else if (hk="Left") || this.State("Left")
      (ww>1 && ww--), hk:=""
    else if (hk="Right") || this.State("Right")
      ww++, hk:=""
    x:=(keydown?x1:x2), y:=(keydown?y1:y2)
    this.RangeTip(x-ww, y-hh, 2*ww+1, 2*hh+1, (A_MSec<500?"Red":"Blue"))
    if (oldx=x2 && oldy=y2)
      Continue
    oldx:=x2, oldy:=y2
    ToolTip % "x: " x " y: " y "`n" tip
  }
  ToolTip
  this.RangeTip()
  Hotkey, IfWinExist, GetRange_HotkeyIf
  For k,v in StrSplit(keys, "|")
    Hotkey, *%v%, %KeyOff%, Off UseErrorLevel
  Hotkey, IfWinExist
  GetRange_HotkeyIf.Destroy()
  Critical % cri
  return [x-ww, y-hh, x+ww, y+hh, Bind_ID]
}

GetRange2(key:="LButton")
{
  local
  FindText_GetRange:=_Gui:=Gui()
  _Gui.Opt("+LastFound +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
  _Gui.BackColor:="White"
  WinSet, Transparent, 10
  this.BitmapFromScreen(,,,,0,x,y,w,h)
  _Gui.Title:="FindText_GetRange"
  _Gui.Show("NA x" x " y" y " w" w " h" h)
  CoordMode, Mouse
  tip:=this.Lang("s7"), oldx:=oldy:=""
  Loop
  {
    Sleep 50
    MouseGetPos, x1, y1
    if (oldx=x1 && oldy=y1)
      Continue
    oldx:=x1, oldy:=y1
    ToolTip % "x: " x1 " y: " y1 " w: 0 h: 0`n" tip
  }
  Until this.State(key) || this.State("Ctrl")
  Loop
  {
    Sleep 50
    MouseGetPos, x2, y2
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
    this.RangeTip(x, y, w, h, (A_MSec<500 ? "Red":"Blue"))
    if (oldx=x2 && oldy=y2)
      Continue
    oldx:=x2, oldy:=y2
    ToolTip % "x: " x " y: " y " w: " w " h: " h "`n" tip
  }
  Until !(this.State(key) || this.State("Ctrl"))
  ToolTip
  this.RangeTip()
  FindText_GetRange.Destroy()
  Clipboard:=x ", " y ", " (x+w-1) ", " (y+h-1)
  return [x, y, x+w-1, y+h-1]
}

BitmapFromScreen(ByRef x:=0, ByRef y:=0, ByRef w:=0, ByRef h:=0
		, ScreenShot:=1, ByRef zx:=0, ByRef zy:=0, ByRef zw:=0, ByRef zh:=0)
{
  local
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy,zw,zh)
  if (w<1 || h<1 || !bits.hBM)
    return
  hBM:=this.CreateDIBSection(w, h)
  this.CopyHBM(hBM, 0, 0, bits.hBM, x-zx, y-zy, w, h, 1)
  return hBM
}

	; 快速保存截图为BMP文件,可用于调试
	; 如果 file = 0 或 "" ,会保存到剪贴板

SavePic(file:=0, x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  hBM:=this.BitmapFromScreen(x, y, w, h, ScreenShot)
  this.SaveBitmapToFile(file, hBM)
  DllCall("DeleteObject", "Ptr",hBM)
}

	; 保存图像到文件,如果 file = 0 或者 "",保存到剪贴板
	; 参数可以是位图句柄或者文件路径,例如: "c:\a.bmp"

SaveBitmapToFile(file, hBM_or_file, x:=0, y:=0, w:=0, h:=0)
{
  local
  if hBM_or_file is number
    hBM_or_file:="HBITMAP:*" hBM_or_file
  if !hBM:=DllCall("CopyImage", "Ptr",LoadPicture(hBM_or_file)
		, "int",0, "int",0, "int",0, "uint",0x2008)
    return
  if (file) || (w!=0 && h!=0)
  {
    (w=0 || h=0) && this.GetBitmapWH(hBM, w, h)
    hBM2:=this.CreateDIBSection(w, -h, bpp:=(file ? 24 : 32))
    this.CopyHBM(hBM2, 0, 0, hBM, x, y, w, h)
    DllCall("DeleteObject", "Ptr",hBM), hBM:=hBM2
  }
  VarSetCapacity(dib, dib_size:=(A_PtrSize=8 ? 104:84), 0)
		, DllCall("GetObject", "Ptr",hBM, "int",dib_size, "Ptr",&dib)
		, pbi:=&dib+(bitmap_size:=A_PtrSize=8 ? 32:24)
		, size:=NumGet(pbi+20, "uint"), pBits:=NumGet(pbi-A_PtrSize, "Ptr")
  if (!file)
  {
    hdib:=DllCall("GlobalAlloc", "uint",2, "Ptr",40+size, "Ptr")
    pdib:=DllCall("GlobalLock", "Ptr",hdib, "Ptr")
    DllCall("RtlMoveMemory", "Ptr",pdib, "Ptr",pbi, "Ptr",40)
    DllCall("RtlMoveMemory", "Ptr",pdib+40, "Ptr",pBits, "Ptr",size)
    DllCall("GlobalUnlock", "Ptr",hdib)
    DllCall("OpenClipboard", "Ptr",0)
    DllCall("EmptyClipboard")
    if !DllCall("SetClipboardData", "uint",8, "Ptr",hdib)
      DllCall("GlobalFree", "Ptr",hdib)
    DllCall("CloseClipboard")
  }
  else
  {
    if InStr(file,"\") && !FileExist(dir:=RegExReplace(file,"[^\\]*$"))
      Try FileCreateDir, % dir
    VarSetCapacity(bf, 14, 0), NumPut(0x4D42, bf, "short")
    NumPut(54+size, bf, 2, "uint"), NumPut(54, bf, 10, "uint")
    f:=FileOpen(file, "w"), f.RawWrite(bf, 14)
		, f.RawWrite(pbi+0, 40), f.RawWrite(pBits+0, size), f.Close()
  }
  DllCall("DeleteObject", "Ptr",hBM)
}

	; 显示保存的图像

ShowPic(file:="", show:=1, ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="")
{
  local
  if (file="")
  {
    this.ShowScreenShot()
    return
  }
  if !(hBM:=LoadPicture(file))
    return
  this.GetBitmapWH(hBM, w, h)
  bits:=this.GetBitsFromScreen(,,,,0,x,y,zw,zh)
  this.UpdateBits(bits, x, y, Max(w,zw), Max(h,zh))
  this.CopyHBM(bits.hBM, 0, 0, hBM, 0, 0, w, h)
  DllCall("DeleteObject", "Ptr",hBM)
  if (show)
    this.ShowScreenShot(x, y, x+w-1, y+h-1, 0)
}

	; 显示内存中的屏幕截图用于调试

ShowScreenShot(x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  static init, hPic, oldx, oldy, oldw, oldh, FindText_Screen
  if !VarSetCapacity(init) && (init:="1")
    FindText_Screen:=""
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
  {
    if (FindText_Screen)
      FindText_Screen.Destroy(), FindText_Screen:=""
    return
  }
  x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  if !hBM:=this.BitmapFromScreen(x,y,w,h,ScreenShot)
    return
	;---------------
  if (!FindText_Screen)
  {
    FindText_Screen:=_Gui:=Gui()  ; WS_EX_NOACTIVATE:=0x08000000
    _Gui.Opt("+AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
    _Gui.MarginX:=0, _Gui.MarginY:=0
    id:=_Gui.Add("Pic", "w" w " h" h), hPic:=id.Hwnd
    _Gui.Title:="Show Pic"
    _Gui.Show("NA x" x " y" y " w" w " h" h)
    oldx:=x, oldy:=y, oldw:=w, oldh:=h
  }
  else if (oldx!=x || oldy!=y || oldw!=w || oldh!=h)
  {
    if (oldw!=w || oldh!=h)
      FindText_Screen[hPic].Move(,, w, h)
    FindText_Screen.Show("NA x" x " y" y " w" w " h" h)
    oldx:=x, oldy:=y, oldw:=w, oldh:=h
  }
  this.BitmapToWindow(hPic, 0, 0, hBM, 0, 0, w, h)
  DllCall("DeleteObject", "Ptr",hBM)
}

BitmapToWindow(hwnd, x1, y1, hBM, x2, y2, w, h)
{
  local
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM, "Ptr")
  hDC:=DllCall("GetDC", "Ptr",hwnd, "Ptr")
  DllCall("BitBlt", "Ptr",hDC, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC, "int",x2, "int",y2, "uint",0xCC0020)
  DllCall("ReleaseDC", "Ptr",hwnd, "Ptr",hDC)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteDC", "Ptr",mDC)
}

	; 快速获取屏幕图像的搜索文本数据

GetTextFromScreen(x1:=0, y1:=0, x2:=0, y2:=0, Threshold:=""
		, ScreenShot:=1, ByRef rx:="", ByRef ry:="", cut:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    return this.Gui("CaptureS", ScreenShot)
  SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
  x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy)
  if (w<1 || h<1 || !bits.Scan0)
  {
    SetBatchLines % bch
    return
  }
  ListLines % (lls:=A_ListLines)?0:0
  gs:=[]
  j:=bits.Stride-w*4, p:=bits.Scan0+(y-zy)*bits.Stride+(x-zx)*4-j-4
  Loop % h + 0*(k:=0)
  Loop % w + 0*(p+=j)
    c:=NumGet(0|p+=4,"uint")
		, gs[++k]:=(((c>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
  if InStr(Threshold,"**")
  {
    Threshold:=StrReplace(Threshold,"*")
    if (Threshold="")
      Threshold:=50
    s:="", sw:=w, w-=2, h-=2, x++, y++
    Loop % h + 0*(y1:=0)
    Loop % w + 0*(y1++)
      i:=y1*sw+A_Index+1, j:=gs[i]+Threshold
		, s.=( gs[i-1]>j || gs[i+1]>j
		|| gs[i-sw]>j || gs[i+sw]>j
		|| gs[i-sw-1]>j || gs[i-sw+1]>j
		|| gs[i+sw-1]>j || gs[i+sw+1]>j ) ? "1":"0"
    Threshold:="**" Threshold
  }
  else
  {
    Threshold:=StrReplace(Threshold,"*")
    if (Threshold="")
    {
      pp:=[]
      Loop 256
        pp[A_Index-1]:=0
      Loop % w*h
        pp[gs[A_Index]]++
      IP0:=IS0:=0
      Loop 256
        k:=A_Index-1, IP0+=k*pp[k], IS0+=pp[k]
      Threshold:=Floor(IP0/IS0)
      Loop 20
      {
        LastThreshold:=Threshold
        IP1:=IS1:=0
        Loop % LastThreshold+1
          k:=A_Index-1, IP1+=k*pp[k], IS1+=pp[k]
        IP2:=IP0-IP1, IS2:=IS0-IS1
        if (IS1!=0 && IS2!=0)
          Threshold:=Floor((IP1/IS1+IP2/IS2)/2)
        if (Threshold=LastThreshold)
          Break
      }
    }
    s:=""
    Loop % w*h
      s.=gs[A_Index]<=Threshold ? "1":"0"
    Threshold:="*" Threshold
  }
  ListLines % lls
	;--------------------
  w:=Format("{:d}",w), CutUp:=CutDown:=0
  if (cut=1)
  {
    re1:="(^0{" w "}|^1{" w "})"
    re2:="(0{" w "}$|1{" w "}$)"
    While (s~=re1)
      s:=RegExReplace(s,re1), CutUp++
    While (s~=re2)
      s:=RegExReplace(s,re2), CutDown++
  }
  rx:=x+w//2, ry:=y+CutUp+(h-CutUp-CutDown)//2
  s:="|<>" Threshold "$" w "." this.bit2base64(s)
	;--------------------
  SetBatchLines % bch
  return s
}

	; 等待几秒钟直到屏幕图像改变,需要先调用FindText().ScreenShot()

WaitChange(time:=-1, x1:=0, y1:=0, x2:=0, y2:=0)
{
  local
  hash:=this.GetPicHash(x1, y1, x2, y2, 0)
  time:=this.Floor(time), timeout:=A_TickCount+Round(time*1000)
  Loop
  {
    if (hash!=this.GetPicHash(x1, y1, x2, y2, 1))
      return 1
    if (time>=0 && A_TickCount>=timeout)
      Break
    Sleep 10
  }
  return 0
}

	; 等待屏幕图像稳定下来

WaitNotChange(time:=1, timeout:=30, x1:=0, y1:=0, x2:=0, y2:=0)
{
  local
  oldhash:="", time:=this.Floor(time)
		, timeout:=A_TickCount+Round(this.Floor(timeout)*1000)
  Loop
  {
    hash:=this.GetPicHash(x1, y1, x2, y2, 1), t:=A_TickCount
    if (hash!=oldhash)
      oldhash:=hash, timeout2:=t+Round(time*1000)
    if (t>=timeout2)
      return 1
    if (t>=timeout)
      return 0
    Sleep 100
  }
}

GetPicHash(x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  static init:=DllCall("LoadLibrary", "Str","ntdll", "Ptr")
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
  if (w<1 || h<1 || !bits.Scan0)
    return 0
  hash:=0, Stride:=bits.Stride, p:=bits.Scan0+(y-1)*Stride+x*4, w*=4
  ListLines % (lls:=A_ListLines)?0:0
  Loop % h
    hash:=(hash*31+DllCall("ntdll\RtlComputeCrc32", "uint",0
		, "Ptr",p+=Stride, "uint",w, "uint"))&0xFFFFFFFF
  ListLines % lls
  return hash
}

WindowToScreen(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  if (!id)
    WinGet, id, ID, A
  VarSetCapacity(rect, 16, 0)
		, DllCall("GetWindowRect", "Ptr",id, "Ptr",&rect)
		, x:=x1+NumGet(rect,"int"), y:=y1+NumGet(rect,4,"int")
}

ScreenToWindow(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  this.WindowToScreen(dx, dy, 0, 0, id), x:=x1-dx, y:=y1-dy
}

ClientToScreen(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  if (!id)
    WinGet, id, ID, A
  VarSetCapacity(pt, 8, 0), NumPut(0, pt, "int64")
		, DllCall("ClientToScreen", "Ptr",id, "Ptr",&pt)
		, x:=x1+NumGet(pt,"int"), y:=y1+NumGet(pt,4,"int")
}

ScreenToClient(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  this.ClientToScreen(dx, dy, 0, 0, id), x:=x1-dx, y:=y1-dy
}

	; 不像 FindText 总是使用屏幕坐标,它使用与内置命令
	; ImageSearch 一样的 CoordMode 设置的坐标模式
	; 图片文件参数可以使用 "*n *TransRRGGBB-RRGGBB-White... d:\a.bmp"

ImageSearch(ByRef rx:="", ByRef ry:="", x1:=0, y1:=0, x2:=0, y2:=0
		, ImageFile:="", ScreenShot:=1, FindAll:=0, dir:=1)
{
  local
  dx:=dy:=0
  if (A_CoordModePixel="Window")
    this.WindowToScreen(dx, dy, 0, 0)
  else if (A_CoordModePixel="Client")
    this.ClientToScreen(dx, dy, 0, 0)
  text:=""
  Loop Parse, ImageFile, |
  if (v:=Trim(A_LoopField))!=""
  {
    text.=InStr(v,"$") ? "|" v : "|##"
    . (RegExMatch(v, "O)(^|\s)\*(\d+)\s", r)
    ? Format("{:06X}", r[2]<<16|r[2]<<8|r[2]) : "000000")
    . (RegExMatch(v, "Oi)(^|\s)\*Trans([\-\w]+)\s", r)
    ? "-" . Trim(r[2],"-") : "") . "$"
    . Trim(RegExReplace(v, "(^|\s)\*\S+"))
  }
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x1:=y1:=-n, x2:=y2:=n
  if (ok:=this.FindText(,, x1+dx, y1+dy, x2+dx, y2+dy
		, 0, 0, text, ScreenShot, FindAll,,,, dir))
  {
    For k,v in ok  ; you can use ok:=FindText().ok
      v.1-=dx, v.2-=dy, v.x-=dx, v.y-=dy
    rx:=ok[1].1, ry:=ok[1].2, ErrorLevel:=0
    return ok
  }
  else
  {
    rx:=ry:="", ErrorLevel:=1
    return 0
  }
}

	; 不像 FindText 总是使用屏幕坐标,它使用与内置命令
	; PixelSearch 一样的 CoordMode 设置的坐标模式
	; 颜色参数可以是 "RRGGBB-DRDGDB|RRGGBB-DRDGDB", Variation 取值 0-255

PixelSearch(ByRef rx:="", ByRef ry:="", x1:=0, y1:=0, x2:=0, y2:=0
		, ColorID:="", Variation:=0, ScreenShot:=1, FindAll:=0, dir:=1)
{
  local
  n:=this.Floor(Variation), text:=Format("##{:06X}$0/0/", n<<16|n<<8|n)
  . Trim(StrReplace(ColorID, "|", "/"), "/")
  return this.ImageSearch(rx, ry, x1, y1, x2, y2, text, ScreenShot, FindAll, dir)
}

	; 屏幕坐标指示的范围内的某些颜色的像素计数
	; 颜色参数可以是 "RRGGBB-DRDGDB|RRGGBB-DRDGDB", Variation 取值 0-255

PixelCount(x1:=0, y1:=0, x2:=0, y2:=0, ColorID:="", Variation:=0, ScreenShot:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
  sum:=0, VarSetCapacity(s1,4), VarSetCapacity(s0,4)
  ini:={ bits:bits, ss:0, s1:&s1, s0:&s0
		, err1:0, err0:0, allpos_max:0, zoomW:1, zoomH:1 }
  n:=this.Floor(Variation), text:=Format("##{:06X}$0/0/", n<<16|n<<8|n)
  . Trim(StrReplace(ColorID, "|", "/"), "/")
  if (w>0 && h>0 && bits.Scan0) && IsObject(j:=this.PicInfo(text))
    sum:=this.PicFind(ini, j, 1, x, y, w, h, 0)
  return sum
}

	; 创建包含特定颜色的色块,可以限定这个色块中符合颜色的数量
	; ColorID 可以使用 "RRGGBB1@0.8|RRGGBB2-DRDGDB2"
	; Count 是这个色块中符合颜色的数量的最小值

ColorBlock(ColorID, w, h, Count)
{
  local
  Text:="|<>[" (1-Count/(w*h)) ",1]"
  . Trim(StrReplace(ColorID, "|", "/"), "/") . Format("${:d}.",w)
  . this.bit2base64(StrReplace(Format(Format("}:0{:d}d{",w*h),0),"0","1"))
  return Text
}

Click(x:="", y:="", other1:="", other2:="", GoBack:=0)
{
  local
  CoordMode, Mouse, % (bak:=A_CoordModeMouse)?"Screen":"Screen"
  if GoBack
    MouseGetPos, oldx, oldy
  MouseMove, x, y, 0
  Click % x "," y "," other1 "," other2
  if GoBack
    MouseMove, oldx, oldy, 0
  CoordMode, Mouse, %bak%
  return 1
}

	; 使用 ControlClick 代替 Click, 使用屏幕坐标,如果用于后台请提供 hwnd

ControlClick(x, y, WhichButton:="", ClickCount:=1, Opt:="", hwnd:="")
{
  local
  if !hwnd
    hwnd:=DllCall("WindowFromPoint", "int64",y<<32|x&0xFFFFFFFF, "Ptr")
  VarSetCapacity(pt,8,0), ScreenX:=x, ScreenY:=y
  Loop
  {
    NumPut(0,pt,"int64"), DllCall("ClientToScreen", "Ptr",hwnd, "Ptr",&pt)
		, x:=ScreenX-NumGet(pt,"int"), y:=ScreenY-NumGet(pt,4,"int")
		, id:=DllCall("ChildWindowFromPoint", "Ptr",hwnd, "int64",y<<32|x, "Ptr")
    if (!id || id=hwnd)
      Break
    else hwnd:=id
  }
  DetectHiddenWindows % (bak:=A_DetectHiddenWindows)?1:1
  PostMessage, 0x200, 0, y<<16|x,, ahk_id %hwnd%  ; WM_MOUSEMOVE
  SetControlDelay -1
  ControlClick, x%x% y%y%, ahk_id %hwnd%,, %WhichButton%, %ClickCount%, NA Pos %Opt%
  DetectHiddenWindows % bak
  return 1
}

	; 动态运行AHK代码作为新线程

Class Thread
{
  __New(args*)
  {
    this.pid:=this.Exec(args*)
  }
  __Delete()
  {
    Process, Close, % this.pid
  }
  Exec(s, Ahk:="", args:="")    ; required AHK v1.1.34+ and Ahk2Exe Use .exe
  {
    local
    Ahk:=Ahk ? Ahk : A_IsCompiled ? A_ScriptFullPath : A_AhkPath
    s:="`nDllCall(""SetWindowText"",""Ptr"",A_ScriptHwnd,""Str"",""<AHK>"")`n"
      . "`nSetBatchLines,-1`n" . s, s:=RegExReplace(s, "\R", "`r`n")
    Try
    {
      shell:=ComObjCreate("WScript.Shell")
      oExec:=shell.Exec("""" Ahk """ /script /force /CP0 * " args)
      oExec.StdIn.Write(s)
      oExec.StdIn.Close(), pid:=oExec.ProcessID
    }
    Catch
    {
      f:=A_Temp "\~ahk.tmp"
      s:="`r`nTry FileDelete " f "`r`n" s
      Try FileDelete %f%
      FileAppend %s%, %f%
      r:=this.Clear.Bind(this)
      SetTimer %r%, -3000
      Run "%Ahk%" /script /force /CP0 "%f%" %args%,, UseErrorLevel, pid
    }
    return pid
  }
  Clear()
  {
    Try FileDelete % A_Temp "\~ahk.tmp"
    SetTimer,, Off
  }
}

	; FindText().QPC() 用法类似于 A_TickCount

QPC()
{
  static init, f, c
  if !VarSetCapacity(init) && (init:="1")
    f:=0, c:=DllCall("QueryPerformanceFrequency", "Int64*",f)+(f/=1000)
  return (!DllCall("QueryPerformanceCounter","Int64*",c))*0+(c/f)
}

	; FindText().ToolTip() 用法类似于 ToolTip

ToolTip(s:="", x:="", y:="", num:=1, arg:="")
{
  local
  static init, ini, tip, timer
  if !VarSetCapacity(init) && (init:="1")
    ini:=[], tip:=[], timer:=[]
  f:="ToolTip_" . this.Floor(num)
  if (s="")
  {
    Try tip[f].Destroy()
    ini[f]:="", tip[f]:=""
    return
  }
	;-----------------
  r1:=A_CoordModeToolTip
  r2:=A_CoordModeMouse
  CoordMode Mouse, Screen
  MouseGetPos x1, y1
  CoordMode Mouse, %r1%
  MouseGetPos x2, y2
  CoordMode Mouse, %r2%
  (x!="" && x:="x" (this.Floor(x)+x1-x2))
		, (y!="" && y:="y" (this.Floor(y)+y1-y2))
		, (x="" && y="" && x:="x" (x1+16) " y" (y1+16))
	;-----------------
  bgcolor:=arg.bgcolor!="" ? arg.bgcolor : "FAFBFC"
  color:=arg.color!="" ? arg.color : "Black"
  font:=arg.font ? arg.font : "Consolas"
  size:=arg.size ? arg.size : "10"
  bold:=arg.bold ? arg.bold : ""
  trans:=arg.trans!="" ? arg.trans & 255 : 255
  timeout:=arg.timeout!="" ? arg.timeout : ""
	;-----------------
  r:=bgcolor "|" color "|" font "|" size "|" bold "|" trans "|" s
  if (!ini.HasKey(f) || ini[f]!=r)
  {
    ini[f]:=r
    Try tip[f].Destroy()
    tip[f]:=_Gui:=Gui()  ; WS_EX_LAYERED:=0x80000, WS_EX_TRANSPARENT:=0x20
    _Gui.Opt("+LastFound +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x80020")
    _Gui.MarginX:=2, _Gui.MarginY:=2
    _Gui.BackColor:=bgcolor
    _Gui.SetFont("c" color " s" size " " bold, font)
    _Gui.Add("Text",, s)
    _Gui.Title:=f
    _Gui.Show("Hide")
    WinSet, Transparent, % trans
  }
  tip[f].Opt("+AlwaysOnTop")
  tip[f].Show("NA " x " " y)
  if (timeout)
  {
    (!timer.HasKey(f) && timer[f]:=this.ToolTip.Bind(this,"","","",num))
		, r:=timer[f]
    SetTimer, %r%, % -Round(Abs(this.Floor(timeout)*1000))-1
  }
}

	; FindText().ObjView() 查看对象的值用于调试

ObjView(obj, keyname:="")
{
  local
  if IsObject(obj)  ; thanks lexikos's type(v)
  {
    s:=""
    For k,v in obj
      s.=this.ObjView(v, keyname "[" (StrLen(k)>1000
		|| [k].GetCapacity(1) ? """" k """":k) "]")
  }
  else
    s:=keyname ": " (StrLen(obj)>1000
		|| [obj].GetCapacity(1) ? """" obj """":obj) "`n"
  if (keyname!="")
    return s
	;------------------
  _Gui:=Gui("+AlwaysOnTop")
  _Gui.Add("Button", "y270 w350 gCancel Default", "OK")
  _Gui.Add("Edit", "xp y10 w350 h250 -Wrap -WantReturn")
  _Gui["Edit1"].Value:=s
  _Gui.Title:="Debug view object values"
  _Gui.Show()
  DetectHiddenWindows 0
  WinWaitClose % "ahk_id " _Gui.Hwnd
  _Gui.Destroy()
}

EditScroll(hEdit, regex:="", line:=0, pos:=0)
{
  local
  ControlGetText, s,, ahk_id %hEdit%
  pos:=(regex!="") ? InStr(SubStr(s,1,s~=regex),"`n",0,0)
: (line>1) ? InStr(s,"`n",0,1,line-1) : pos
  SendMessage, 0xB1, pos, pos,, ahk_id %hEdit%
  SendMessage, 0xB7,,,, ahk_id %hEdit%
}

	; 获取最后添加的Gui控件的对象,前提是 Gui +LastFound

LastCtrl()
{
  local
  return (G:=GuiFromHwnd(WinExist()))[G.LastHwnd]
}

	; 隐藏窗口,前提是 Gui +LastFound

Hide(args*)
{
  WinMinimize
  WinHide
  ToolTip
  DetectHiddenWindows 0
  WinWaitClose % "ahk_id " WinExist()
}


	;==== Optional GUI interface ====


Gui(cmd, arg1:="", args*)
{
  local
  static
  local bch, cri, lls, _Gui
  ListLines, % InStr("MouseMove|ToolTipOff",cmd)?0:A_ListLines
  static init
  if !VarSetCapacity(init) && (init:="1")
  {
    SavePicDir:=A_Temp "\Ahk_ScreenShot\"
    G_ := this.Gui.Bind(this)
    G_G := this.Gui.Bind(this, "G")
    G_Run := this.Gui.Bind(this, "Run")
    G_Show := this.Gui.Bind(this, "Show")
    G_KeyDown := this.Gui.Bind(this, "KeyDown")
    G_LButtonDown := this.Gui.Bind(this, "LButtonDown")
    G_RButtonDown := this.Gui.Bind(this, "RButtonDown")
    G_MouseMove := this.Gui.Bind(this, "MouseMove")
    G_ScreenShot := this.Gui.Bind(this, "ScreenShot")
    G_ShowPic := this.Gui.Bind(this, "ShowPic")
    G_Slider := this.Gui.Bind(this, "Slider")
    G_ToolTip := this.Gui.Bind(this, "ToolTip")
    G_ToolTipOff := this.Gui.Bind(this, "ToolTipOff")
    G_SaveScr := this.Gui.Bind(this, "SaveScr")
    FindText_Capture:=FindText_Main:=FindText_SubPic:=""
    bch:=A_BatchLines, cri:=A_IsCritical
    Critical
#NoEnv
    Lang:=this.Lang(,1), Tip_Text:=this.Lang(,2)
    G_.Call("MakeCaptureWindow")
    G_.Call("MakeMainWindow")
    OnMessage(0x100, G_KeyDown)
    OnMessage(0x201, G_LButtonDown)
    OnMessage(0x204, G_RButtonDown)
    OnMessage(0x200, G_MouseMove)
    Menu, Tray, Add
    Menu, Tray, Add, % Lang["s1"], % G_Show
    if (!A_IsCompiled && A_LineFile=A_ScriptFullPath)
    {
      Menu, Tray, Default, % Lang["s1"]
      Menu, Tray, Click, 1
      Menu, Tray, Icon, Shell32.dll, 23
    }
    Critical % cri
    SetBatchLines % bch
    Gui("+LastFound").Destroy()
	;-------------------
    Pics:=PrevControl:=x:=y:=oldx:=oldy:="", dx:=dy:=oldt:=0
  }
  Switch cmd
  {
  Case "G":
    id:=this.LastCtrl()
    Try id.OnEvent("Click", G_Run)
    Catch
      Try id.OnEvent("Change", G_Run)
    return
  Case "Run":
    Critical
    G_.Call(arg1.Name)
    return
  Case "Show":
    FindText_Main.Show(arg1 ? "Center" : "")
    ControlFocus,, % "ahk_id " hscr
    return
  Case "Cancel", "Cancel2":
    WinHide
    return
  Case "MakeCaptureWindow":
    WindowColor:="0xDDEEFF"
    Try FindText_Capture.Destroy()
    FindText_Capture:=_Gui:=Gui()
    _Gui.Opt("+LastFound +AlwaysOnTop -DPIScale")
    _Gui.MarginX:=15, _Gui.MarginY:=10
    _Gui.BackColor:=WindowColor
    _Gui.SetFont("s12", "Verdana")
    Tab:=_Gui.Add("Tab3", "vMyTab1 -Wrap", StrSplit(Lang["s18"],"|"))
    Tab.UseTab(1)
    C_:=[], nW:=71, nH:=25, w:=h:=12, pW:=nW*(w+1)-1, pH:=(nH+1)*(h+1)-1
    _Gui.Opt("-Theme")
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nW*(nH+1)
    {
      i:=A_Index, j:=i=1 ? "Section" : Mod(i,nW)=1 ? "xs y+1":"x+1"
      id:=_Gui.Add("Progress", j " w" w " h" h " -E0x20000 Smooth")
      C_[i]:=id.Hwnd
    }
    ListLines % lls
    _Gui.Opt("+Theme")
    _Gui.Add("Slider", "xs w" pW " vMySlider1 +Center Page20 Line10 NoTicks AltSubmit")
    G_G.Call()
    _Gui.Add("Slider", "ys h" pH " vMySlider2 +Center Page20 Line10 NoTicks AltSubmit +Vertical")
    G_G.Call()
    Tab.UseTab(2)
    id:=_Gui.Add("Text", "w" (pW-135) " h" pH " +Border Section"), parent_id:=id.Hwnd
    _Gui.Add("Slider", "xs wp vMySlider3 +Center Page20 Line10 NoTicks AltSubmit")
    G_G.Call()
    _Gui.Add("Slider", "ys h" pH " vMySlider4 +Center Page20 Line10 NoTicks AltSubmit +Vertical")
    G_G.Call()
    _Gui.Add("ListBox", "ys w120 h200 vSelectBox AltSubmit 0x100")
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vClearAll", Lang["ClearAll"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vOpenDir", Lang["OpenDir"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vLoadPic", Lang["LoadPic"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vSavePic", Lang["SavePic"])
    G_G.Call()
    Tab.UseTab()
	;--------------
    _Gui.Add("Text", "xm Section", Lang["SelGray"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelGray ReadOnly")
    _Gui.Add("Text", "x+15 ys", Lang["SelColor"])
    _Gui.Add("Edit", "x+5 yp-3 w150 vSelColor ReadOnly")
    _Gui.Add("Text", "x+15 ys", Lang["SelR"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelR ReadOnly")
    _Gui.Add("Text", "x+5 ys", Lang["SelG"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelG ReadOnly")
    _Gui.Add("Text", "x+5 ys", Lang["SelB"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelB ReadOnly")
	;--------------
    id:=_Gui.Add("Button", "xm Hidden Section", Lang["Auto"])
    id.GetPos(pX, pY, pW, pH)
    w:=Round(pW*0.75), i:=Round(w*3+15+pW*0.5-w*1.5)
    _Gui.Add("Button", "xm+" i " yp w" w " hp -Wrap vRepU", Lang["RepU"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutU", Lang["CutU"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutU3", Lang["CutU3"])
    G_G.Call()
    _Gui.Add("Button", "xm wp hp -Wrap vRepL", Lang["RepL"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutL", Lang["CutL"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutL3", Lang["CutL3"])
    G_G.Call()
    _Gui.Add("Button", "x+15 w" pW " hp -Wrap vAuto", Lang["Auto"])
    G_G.Call()
    _Gui.Add("Button", "x+15 w" w " hp -Wrap vRepR", Lang["RepR"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutR", Lang["CutR"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutR3", Lang["CutR3"])
    G_G.Call()
    _Gui.Add("Button", "xm+" i " wp hp -Wrap vRepD", Lang["RepD"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutD", Lang["CutD"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutD3", Lang["CutD3"])
    G_G.Call()
	;--------------
    Tab:=_Gui.Add("Tab3", "ys -Wrap", StrSplit(Lang["s2"],"|"))
    Tab.UseTab(1)
    _Gui.Add("Text", "x+30 y+35", Lang["Threshold"])
    _Gui.Add("Edit", "x+15 w100 vThreshold")
    _Gui.Add("Button", "x+15 yp-3 vGray2Two", Lang["Gray2Two"])
    G_G.Call()
    Tab.UseTab(2)
    _Gui.Add("Text", "x+30 y+35", Lang["GrayDiff"])
    _Gui.Add("Edit", "x+15 w100 vGrayDiff", "50")
    _Gui.Add("Button", "x+15 yp-3 vGrayDiff2Two", Lang["GrayDiff2Two"])
    G_G.Call()
    Tab.UseTab(3)
    _Gui.Add("Text", "x+10 y+15 Section", Lang["Similar1"])
    _Gui.Add("Edit", "x+5 w80 vSimilar1 Limit3")
    _Gui.Add("UpDown", "vSim Range0-100", 90)
    _Gui.Add("Button", "x+10 ys-2 vAddColorSim", Lang["AddColorSim"])
    G_G.Call()
    _Gui.Add("Text", "x+20 ys+4", Lang["DiffRGB2"])
    _Gui.Add("Edit", "x+5 ys w80 vDiffRGB2 Limit3")
    _Gui.Add("UpDown", "vdRGB2 Range0-255 Wrap", 16)
    _Gui.Add("Button", "x+10 ys-2 vAddColorDiff", Lang["AddColorDiff"])
    G_G.Call()
    _Gui.Add("Button", "xs vUndo2", Lang["Undo2"])
    G_G.Call()
    _Gui.Add("Edit", "x+10 yp+2 w340 vColorList")
    _Gui.Add("Button", "x+10 yp-2 vColor2Two", Lang["Color2Two"])
    G_G.Call()
    Tab.UseTab(4)
    _Gui.Add("Text", "x+30 y+35", Lang["Similar2"] " 0")
    _Gui.Add("Slider", "x+0 w120 vSimilar2 +Center Page1 NoTicks ToolTip", 90)
    _Gui.Add("Text", "x+0", "100")
    _Gui.Add("Button", "x+15 yp-3 vColorPos2Two", Lang["ColorPos2Two"])
    G_G.Call()
    Tab.UseTab(5)
    _Gui.Add("Text", "x+30 y+35", Lang["DiffRGB"])
    _Gui.Add("Edit", "x+5 w80 vDiffRGB Limit3")
    _Gui.Add("UpDown", "vdRGB Range0-255 Wrap", 16)
    _Gui.Add("Checkbox", "x+15 yp+5 vMultiColor", Lang["MultiColor"])
    G_G.Call()
    _Gui.Add("Button", "x+15 yp-5 vUndo", Lang["Undo"])
    G_G.Call()
    Tab.UseTab()
	;--------------
    _Gui.Add("Button", "xm vReset", Lang["Reset"])
    G_G.Call()
    _Gui.Add("Checkbox", "x+15 yp+5 vModify", Lang["Modify"])
    G_G.Call()
    _Gui.Add("Text", "x+30", Lang["Comment"])
    _Gui.Add("Edit", "x+5 yp-2 w250 vComment")
    _Gui.Add("Button", "x+10 yp-3 vSplitAdd", Lang["SplitAdd"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vAllAdd", Lang["AllAdd"])
    G_G.Call()
    _Gui.Add("Button", "x+30 wp vOK", Lang["OK"])
    G_G.Call()
    _Gui.Add("Button", "x+15 wp vCancel", Lang["Cancel"])
    G_G.Call()
    _Gui.Add("Button", "xm vBind0", Lang["Bind0"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind1", Lang["Bind1"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind2", Lang["Bind2"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind3", Lang["Bind3"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind4", Lang["Bind4"])
    G_G.Call()
    _Gui.Add("Button", "x+30 vSavePic2", Lang["SavePic2"])
    G_G.Call()
    _Gui.Title:=Lang["s3"]
    _Gui.Show("Hide")
	;--------------------
    Try FindText_SubPic.Destroy()
    FindText_SubPic:=_Gui:=Gui()  ; Don't use +AlwaysOnTop
    _Gui.Opt("+Parent" parent_id " -Caption +ToolWindow -DPIScale")
    _Gui.MarginX:=0, _Gui.MarginY:=0
    _Gui.BackColor:="White"
    id:=_Gui.Add("Pic", "x0 y0 w100 h100"), sub_hpic:=id.Hwnd
    _Gui.Title:="SubPic"
    _Gui.Show("Hide")
    return
  Case "MakeMainWindow":
    Try FindText_Main.Destroy()
    FindText_Main:=_Gui:=Gui()
    _Gui.Opt("+LastFound +AlwaysOnTop -DPIScale")
    _Gui.MarginX:=15, _Gui.MarginY:=10
    _Gui.BackColor:=WindowColor
    _Gui.SetFont("s12", "Verdana")
    _Gui.Add("Text", "xm", Lang["NowHotkey"])
    _Gui.Add("Edit", "x+5 w160 vNowHotkey ReadOnly")
    _Gui.Add("Hotkey", "x+5 w160 vSetHotkey1")
    s:="F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12|LWin|Ctrl|Shift|Space|MButton"
      . "|ScrollLock|CapsLock|Ins|Esc|BS|Del|Tab|Home|End|PgUp|PgDn"
      . "|NumpadDot|NumpadSub|NumpadAdd|NumpadDiv|NumpadMult"
    _Gui.Add("DDL", "x+5 w160 vSetHotkey2", StrSplit(s,"|"))
    _Gui.Add("Button", "x+15 vApply", Lang["Apply"])
    G_G.Call()
    _Gui.Add("GroupBox", "xm y+0 w280 h55 vMyGroup cBlack")
    _Gui.Add("Text", "xp+15 yp+20 Section", Lang["Myww"] ": ")
    _Gui.Add("Text", "x+0 w80", nW//2)
    _Gui.Add("UpDown", "vMyww Range1-100", nW//2)
    _Gui.Add("Text", "x+15 ys", Lang["Myhh"] ": ")
    _Gui.Add("Text", "x+0 w80", nH//2)
    id:=_Gui.Add("UpDown", "vMyhh Range1-100", nH//2)
    id.GetPos(pX, pY, pW, pH)
    _Gui["MyGroup"].Move(,, pX+pW, pH+30)
    id:=_Gui.Add("Checkbox", "x+100 ys vAddFunc", Lang["AddFunc"] " FindText()")
    id.GetPos(pX, pY, pW, pH)
    pW:=pX+pW-15, pW:=(pW<720?720:pW), w:=pW//5
    _Gui.Add("Button", "xm y+18 w" w " vCutL2", Lang["CutL2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutR2", Lang["CutR2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutU2", Lang["CutU2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutD2", Lang["CutD2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vUpdate", Lang["Update"])
    G_G.Call()
    _Gui.SetFont("s6 bold", "Verdana")
    _Gui.Add("Edit", "xm y+10 w" pW " h260 vMyPic -Wrap HScroll")
    _Gui.SetFont("s12 norm", "Verdana")
    w:=pW//3
    _Gui.Add("Button", "xm w" w " vCapture", Lang["Capture"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vTest", Lang["Test"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCopy", Lang["Copy"])
    G_G.Call()
    _Gui.Add("Button", "xm y+0 wp vCaptureS", Lang["CaptureS"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vGetRange", Lang["GetRange"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vGetOffset", Lang["GetOffset"])
    G_G.Call()
    _Gui.Add("Edit", "xm y+10 w130 hp vClipText")
    _Gui.Add("Button", "x+0 vPaste", Lang["Paste"])
    G_G.Call()
    _Gui.Add("Button", "x+0 vTestClip", Lang["TestClip"])
    G_G.Call()
    id:=_Gui.Add("Button", "x+0 vGetClipOffset", Lang["GetClipOffset"])
    G_G.Call()
    id.GetPos(x,, w)
    w:=((pW+15)-(x+w))//2
    _Gui.Add("Edit", "x+0 w" w " hp vOffset")
    _Gui.Add("Button", "x+0 wp vCopyOffset", Lang["CopyOffset"])
    G_G.Call()
    _Gui.SetFont("cBlue")
    id:=_Gui.Add("Edit", "xm w" pW " h250 vscr -Wrap HScroll"), hscr:=id.Hwnd
    _Gui.Title:=Lang["s4"]
    _Gui.Show("Hide")
    G_.Call("LoadScr")
    OnExit(G_SaveScr)
    return
  Case "LoadScr":
    f:=A_Temp "\~scr1.tmp"
    FileRead, s, %f%
    FindText_Main["scr"].Value:=s
    return
  Case "SaveScr":
    f:=A_Temp "\~scr1.tmp"
    s:=FindText_Main["scr"].Value
    Try FileDelete, %f%
    FileAppend, %s%, %f%
    return
  Case "Capture", "CaptureS":
    _Gui:=FindText_Main
    if show_gui:=(WinExist("ahk_id" _Gui.Hwnd))
      this.Hide()
    if (cmd="Capture")
    {
      w:=_Gui["Myww"].Value
      h:=_Gui["Myhh"].Value
      p:=this.GetRange(w, h)
      sx:=p[1], sy:=p[2], sw:=p[3]-p[1]+1, sh:=p[4]-p[2]+1
		, Bind_ID:=p[5], bind_mode:=""
      _Gui:=FindText_Capture
      _Gui["MyTab1"].Choose(1)
    }
    else
    {
      sx:=0, sy:=0, sw:=1, sh:=1, Bind_ID:=WinExist("A"), bind_mode:=""
      _Gui:=FindText_Capture
      _Gui["MyTab1"].Choose(2)
    }
    n:=150000, x:=y:=-n, w:=h:=2*n
    hBM:=this.BitmapFromScreen(x,y,w,h,(arg1=0?0:1))
    G_.Call("CaptureUpdate")
    G_.Call("PicUpdate")
    FindText_SubPic.Show()
    Names:=[], s:=""
    Loop Files, % SavePicDir "*.bmp"
      Names.Push(v:=A_LoopFileFullPath), s.="|" RegExReplace(v,"i)^.*\\|\.bmp$")
    _Gui["SelectBox"].Delete()
    _Gui["SelectBox"].Add(StrSplit(Trim(s,"|"),"|"))
	;------------------------
    s:="SelGray|SelColor|SelR|SelG|SelB|Threshold|Comment|ColorList"
    Loop Parse, s, |
      _Gui[A_LoopField].Value:=""
    _Gui["Modify"].Value:=Modify:=0
    _Gui["MultiColor"].Value:=MultiColor:=0
    _Gui["GrayDiff"].Value:=50
    _Gui["Gray2Two"].Focus()
    _Gui["Gray2Two"].Opt("+Default")
    _Gui.Show("Center")
    Event:=Result:=""
    DetectHiddenWindows 0
    Critical, Off
    WinWaitClose % "ahk_id " _Gui.Hwnd
    Critical
    ToolTip
    FindText_SubPic.Hide()
    _Gui:=FindText_Main
    Clipboard:=Text:=RegExMatch(Result,"O)\|<[^>\n]*>[^$\n]+\$[^""\r\n]+",r)?r[0]:""
	;------------------------
    if (bind_mode!="")
    {
      WinGetTitle, tt, ahk_id %Bind_ID%
      WinGetClass, tc, ahk_id %Bind_ID%
      tt:=Trim(SubStr(tt,1,30) (tc ? " ahk_class " tc:""))
      tt:=StrReplace(RegExReplace(tt,"[;``]","``$0"),"""","""""")
      Result:="`nSetTitleMatchMode 2`nid:=WinExist(""" tt """)"
        . "`nFindText().BindWindow(id" (bind_mode=0 ? "":"," bind_mode)
        . ")  `; " Lang["s6"] " FindText().BindWindow(0)`n`n" Result
    }
    if (Event="OK")
    {
      s:=""
      if (!A_IsCompiled)
        Try FileRead, s, %A_LineFile%
      re:="Oi)\n\s*FindText[^\n]+args\*[\s\S]*"
      s:=RegExMatch(s, re, r) ? "`n;==========`n" r[0] "`n" : ""
      _Gui["scr"].Value:=Result "`n" s
      _Gui["MyPic"].Value:=Trim(this.ASCII(Result),"`n")
    }
    else if (Event="SplitAdd") || (Event="AllAdd")
    {
      s:=_Gui["scr"].Value
      r:=SubStr(s, 1, InStr(s,"=FindText("))
      i:=j:=0, re:="<[^>\n]*>[^$\n]+\$[^""\r\n]+"
      While j:=RegExMatch(r, re,, j+1)
        i:=InStr(r, "`n", 0, j)
      _Gui["scr"].Value:=SubStr(s,1,i) . Result . SubStr(s,i+1)
      _Gui["MyPic"].Value:=Trim(this.ASCII(Result),"`n")
    }
    if (Event) && RegExMatch(Result, "O)\$\d+\.[\w+/]{1,100}", r)
      this.EditScroll(hscr, "\Q" r[0] "\E")
    Event:=Result:=s:=""
	;----------------------
    if (show_gui && arg1="")
      G_Show.Call()
    return Text
  Case "CaptureUpdate":
    nX:=sx, nY:=sy, nW:=sw, nH:=sh
    bits:=this.GetBitsFromScreen(nX,nY,nW,nH,0,zx,zy)
    cors:=[], show:=[], ascii:=[]
		, SelPos:=bg:=color:=""
		, dx:=dy:=CutLeft:=CutRight:=CutUp:=CutDown:=0
    ListLines % (lls:=A_ListLines)?0:0
    if (nW>0 && nH>0 && bits.Scan0)
    {
      j:=bits.Stride-nW*4, p:=bits.Scan0+(nY-zy)*bits.Stride+(nX-zx)*4-j-4
      Loop % nH + 0*(k:=0)
      Loop % nW + 0*(p+=j)
        show[++k]:=1, cors[k]:=NumGet(0|p+=4,"uint")
    }
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    {
      c:=(++tx)<nW && ty<nH ? cors[ty*nW+tx+1] : WindowColor
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[++k]
    }
    Loop % 71 + 0*(k:=71*25)
      SendMessage,0x2001,0,0xAAFFFF,,% "ahk_id " C_[++k]
    ListLines % lls
    _Gui:=FindText_Capture
    _Gui["MySlider1"].Enabled:=nW>71
    _Gui["MySlider2"].Enabled:=nH>25
    _Gui["MySlider1"].Value:=0
    _Gui["MySlider2"].Value:=0
    return
  Case "PicUpdate":
    FindText_SubPic[sub_hpic].Value:="*w0 *h0 HBITMAP:" hBM
    _Gui:=FindText_Capture
    _Gui["MySlider3"].Value:=0
    _Gui["MySlider4"].Value:=0
    G_.Call("MySlider3")
    return
  Case "MySlider3", "MySlider4":
    _Gui:=FindText_Capture
    _Gui[parent_id].GetPos(,, w, h)
    MySlider3:=_Gui["MySlider3"].Value
    MySlider4:=_Gui["MySlider4"].Value
    FindText_SubPic[sub_hpic].GetPos(,, pW, pH)
    x:=pW>w ? -Round((pW-w)*MySlider3/100) : 0
    y:=pH>h ? -Round((pH-h)*MySlider4/100) : 0
    FindText_SubPic.Show("NA x" x " y" y " w" pW " h" pH)
    return
  Case "Reset":
    G_.Call("CaptureUpdate")
    return
  Case "LoadPic":
    FindText_Capture.Opt("+OwnDialogs")
    f:=arg1
    if (f="")
    {
      if !FileExist(SavePicDir)
        FileCreateDir, % SavePicDir
      f:=SavePicDir "*.bmp"
      Loop Files, % f
        f:=A_LoopFileFullPath
      FileSelectFile, f,, %f%, Select Picture
    }
    if !FileExist(f)
    {
      MsgBox, 4096, Tip, % Lang["s17"] " !", 1
      return
    }
    this.ShowPic(f, 0, sx, sy, sw, sh)
    hBM:=this.BitmapFromScreen(sx, sy, sw, sh, 0)
    sw:=Min(sw,200), sh:=Min(sh,200)
    G_.Call("CaptureUpdate")
    G_.Call("PicUpdate")
    return
  Case "SavePic":
    _Gui:=FindText_Capture
    SelectBox:=_Gui["SelectBox"].Value
    Try f:="", f:=Names[SelectBox]
    _Gui.Hide()
    this.ShowPic(f)
    Try GuiFromHwnd(WinExist("Show Pic")).Opt("+OwnDialogs")
    Loop
    {
      p:=this.GetRange2()
      MsgBox, 4100, Tip, % Lang["s15"] " !"
      IfMsgBox, Yes
        Break
    }
    G_.Call("ScreenShot", p[1] "|" p[2] "|" p[3] "|" p[4] "|0")
    this.ShowPic()
    return
  Case "SelectBox":
    SelectBox:=FindText_Capture["SelectBox"].Value
    Try f:="", f:=Names[SelectBox]
    if (f!="")
      G_.Call("LoadPic", f)
    return
  Case "ClearAll":
    FindText_Capture.Hide()
    FileDelete, % SavePicDir "*.bmp"
    return
  Case "OpenDir":
    FindText_Capture.Minimize()
    if !FileExist(SavePicDir)
      FileCreateDir, % SavePicDir
    Run, % SavePicDir
    return
  Case "GetRange":
    _Gui:=FindText_Main
    _Gui.Opt("+LastFound")
    this.Hide()
    p:=this.GetRange2(), v:=p[1] ", " p[2] ", " p[3] ", " p[4]
    s:=_Gui["scr"].Value
    re:="i)(=FindText\([^\n]*?)([^(,\n]*,){4}([^,\n]*,[^,\n]*,[^,\n]*Text)"
    if SubStr(s,1,s~="i)\n\s*FindText[^\n]+args\*")~=re
    {
      s:=RegExReplace(s, re, "$1 " v ",$3",, 1)
      _Gui["scr"].Value:=s
    }
    _Gui["Offset"].Value:=v
    G_Show.Call()
    return
  Case "Test", "TestClip":
    _Gui:=FindText_Main
    _Gui.Opt("+LastFound")
    this.Hide()
	;----------------------
    if (cmd="Test")
      s:=_Gui["scr"].Value
    else
      s:=_Gui["ClipText"].Value
    if (cmd="Test") && InStr(s, "MCode(")
    {
      s:="`n#NoEnv`nMenu, Tray, Click, 1`n" s "`nExitApp`n"
      Thread1:=new this.Thread(s)
      DetectHiddenWindows, 1
      WinWait, % "ahk_class AutoHotkey ahk_pid " Thread1.pid,, 3
      if (!ErrorLevel)
        WinWaitClose,,, 30
	; Thread1:=""  ; kill the Thread
    }
    else
    {
      t:=A_TickCount, v:=X:=Y:=""
      if RegExMatch(s, "O)<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
        v:=this.FindText(X, Y, 0,0,0,0, 0,0, r[0])
      r:=StrSplit(Lang["s8"] "||||", "|")
      MsgBox, 4096, Tip, % r[1] ":`t" (IsObject(v)?v.Length():v) "`n`n"
        . r[2] ":`t" (A_TickCount-t) " " r[3] "`n`n"
        . r[4] ":`t" X ", " Y "`n`n"
        . r[5] ":`t<" (IsObject(v)?v[1].id:"") ">", 3
      Try For i,j in v
        if (i<=2)
          this.MouseTip(j.x, j.y)
      v:="", Clipboard:=X "," Y
    }
	;----------------------
    G_Show.Call()
    return
  Case "GetOffset", "GetClipOffset":
    FindText_Main.Hide()
    p:=this.GetRange()
    _Gui:=FindText_Main
    if (cmd="GetOffset")
      s:=_Gui["scr"].Value
    else
      s:=_Gui["ClipText"].Value
    if RegExMatch(s, "O)<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
		&& this.FindText(X, Y, 0,0,0,0, 0,0, r[0])
    {
      r:=StrReplace("X+" ((p[1]+p[3])//2-X)
        . ", Y+" ((p[2]+p[4])//2-Y), "+-", "-")
      if (cmd="GetOffset")
      {
        re:="i)(\(\)\.\w*Click\w*\()[^,\n]*,[^,)\n]*"
        if SubStr(s,1,s~="i)\n\s*FindText[^\n]+args\*")~=re
          s:=RegExReplace(s, re, "$1" r,, 1)
        _Gui["scr"].Value:=s
      }
      _Gui["Offset"].Value:=r
    }
    s:="", G_Show.Call()
    return
  Case "Paste":
    if RegExMatch(Clipboard, "O)\|?<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
    {
      FindText_Main["ClipText"].Value:=r[0]
      FindText_Main["MyPic"].Value:=Trim(this.ASCII(r[0]),"`n")
    }
    return
  Case "CopyOffset":
    Clipboard:=FindText_Main["Offset"].Value
    return
  Case "Copy":
    ControlGet, s, Selected,,, ahk_id %hscr%
    if (s="")
    {
      s:=FindText_Main["scr"].Value
      r:=FindText_Main["AddFunc"].Value
      if (r != 1)
        s:=RegExReplace(s, "i)\n\s*FindText[^\n]+args\*[\s\S]*")
		, s:=RegExReplace(s, "i)\n; ok:=FindText[\s\S]*")
		, s:=SubStr(s, (s~="i)\n[ \t]*Text"))
    }
    Clipboard:=RegExReplace(s, "\R", "`r`n")
    ControlFocus,, % "ahk_id " hscr
    return
  Case "Apply":
    _Gui:=FindText_Main
    NowHotkey:=_Gui["NowHotkey"].Value
    SetHotkey1:=_Gui["SetHotkey1"].Value
    SetHotkey2:=_Gui["SetHotkey2"].Text
    if (NowHotkey!="")
      Hotkey, *%NowHotkey%,, Off UseErrorLevel
    k:=SetHotkey1!="" ? SetHotkey1 : SetHotkey2
    if (k!="")
      Hotkey, *%k%, %G_ScreenShot%, On UseErrorLevel
    _Gui["NowHotkey"].Value:=k
    _Gui["SetHotkey1"].Value:=""
    _Gui["SetHotkey2"].Choose(0)
    return
  Case "ScreenShot":
    Critical
    if !FileExist(SavePicDir)
      FileCreateDir, % SavePicDir
    Loop
      f:=SavePicDir . Format("{:03d}.bmp",A_Index)
    Until !FileExist(f)
    this.SavePic(f, StrSplit(arg1,"|")*)
    CoordMode, ToolTip
    this.ToolTip(Lang["s9"],, 0,, { bgcolor:"Yellow", color:"Red"
		, size:48, bold:"bold", trans:200, timeout:0.2 })
    return
  Case "Bind0", "Bind1", "Bind2", "Bind3", "Bind4":
    this.BindWindow(Bind_ID, bind_mode:=SubStr(cmd,5))
    n:=150000, x:=y:=-n, w:=h:=2*n
    hBM:=this.BitmapFromScreen(x,y,w,h,1)
    G_.Call("PicUpdate")
    FindText_Capture["MyTab1"].Choose(2)
    this.BindWindow(0)
    return
  Case "MySlider1", "MySlider2":
    SetTimer, %G_Slider%, -10
    return
  Case "Slider":
    Critical
    _Gui:=FindText_Capture
    MySlider1:=_Gui["MySlider1"].Value
    MySlider2:=_Gui["MySlider2"].Value
    dx:=nW>71 ? Round((nW-71)*MySlider1/100) : 0
    dy:=nH>25 ? Round((nH-25)*MySlider2/100) : 0
    if (oldx=dx && oldy=dy)
      return
    ListLines % (lls:=A_ListLines)?0:0
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    {
      c:=((++tx)>=nW || ty>=nH || !show[i:=ty*nW+tx+1]
      ? WindowColor : bg="" ? cors[i] : ascii[i] ? 0 : 0xFFFFFF)
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[++k]
    }
    Loop % 71*(oldx!=dx) + 0*(i:=nW*nH+dx)*(k:=71*25)
      SendMessage,0x2001,0,(show[++i]?0x0000FF:0xAAFFFF),,% "ahk_id " C_[++k]
    ListLines % lls
    oldx:=dx, oldy:=dy
    return
  Case "RepColor", "CutColor":
    if (cmd="RepColor")
      show[k]:=1, c:=(bg="" ? cors[k] : ascii[k] ? 0 : 0xFFFFFF)
    else
      show[k]:=0, c:=WindowColor
    if (tx:=Mod(k-1,nW)-dx)>=0 && tx<71 && (ty:=(k-1)//nW-dy)>=0 && ty<25
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[ty*71+tx+1]
    return
  Case "RepL":
    if (CutLeft<=0) || (bg!="" && InStr(color,"**") && CutLeft=1)
      return
    k:=CutLeft-nW, CutLeft--
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("RepColor"))
    return
  Case "CutL":
    if (CutLeft+CutRight>=nW)
      return
    CutLeft++, k:=CutLeft-nW
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("CutColor"))
    return
  Case "CutL3":
    Loop 3
      G_.Call("CutL")
    return
  Case "RepR":
    if (CutRight<=0) || (bg!="" && InStr(color,"**") && CutRight=1)
      return
    k:=1-CutRight, CutRight--
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("RepColor"))
    return
  Case "CutR":
    if (CutLeft+CutRight>=nW)
      return
    CutRight++, k:=1-CutRight
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("CutColor"))
    return
  Case "CutR3":
    Loop 3
      G_.Call("CutR")
    return
  Case "RepU":
    if (CutUp<=0) || (bg!="" && InStr(color,"**") && CutUp=1)
      return
    k:=(CutUp-1)*nW, CutUp--
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("RepColor"))
    return
  Case "CutU":
    if (CutUp+CutDown>=nH)
      return
    CutUp++, k:=(CutUp-1)*nW
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("CutColor"))
    return
  Case "CutU3":
    Loop 3
      G_.Call("CutU")
    return
  Case "RepD":
    if (CutDown<=0) || (bg!="" && InStr(color,"**") && CutDown=1)
      return
    k:=(nH-CutDown)*nW, CutDown--
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("RepColor"))
    return
  Case "CutD":
    if (CutUp+CutDown>=nH)
      return
    CutDown++, k:=(nH-CutDown)*nW
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("CutColor"))
    return
  Case "CutD3":
    Loop 3
      G_.Call("CutD")
    return
  Case "Gray2Two":
    ListLines % (lls:=A_ListLines)?0:0
    gs:=[], k:=0
    Loop % nW*nH
      gs[++k]:=((((c:=cors[k])>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
    _Gui:=FindText_Capture
    _Gui["Threshold"].Focus()
    Threshold:=_Gui["Threshold"].Value
    if (Threshold="")
    {
      pp:=[]
      Loop 256
        pp[A_Index-1]:=0
      Loop % nW*nH
        if (show[A_Index])
          pp[gs[A_Index]]++
      IP0:=IS0:=0
      Loop 256
        k:=A_Index-1, IP0+=k*pp[k], IS0+=pp[k]
      Threshold:=Floor(IP0/IS0)
      Loop 20
      {
        LastThreshold:=Threshold
        IP1:=IS1:=0
        Loop % LastThreshold+1
          k:=A_Index-1, IP1+=k*pp[k], IS1+=pp[k]
        IP2:=IP0-IP1, IS2:=IS0-IS1
        if (IS1!=0 && IS2!=0)
          Threshold:=Floor((IP1/IS1+IP2/IS2)/2)
        if (Threshold=LastThreshold)
          Break
      }
      _Gui["Threshold"].Value:=Threshold
    }
    Threshold:=Round(Threshold)
    color:="*" Threshold, k:=i:=0
    Loop % nW*nH
      ascii[++k]:=v:=(gs[k]<=Threshold)
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "GrayDiff2Two":
    _Gui:=FindText_Capture
    GrayDiff:=_Gui["GrayDiff"].Value
    if (GrayDiff="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s11"] " !", 1
      return
    }
    ListLines % (lls:=A_ListLines)?0:0
    gs:=[], k:=0
    Loop % nW*nH
      gs[++k]:=((((c:=cors[k])>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
    if (CutLeft=0)
      G_.Call("CutL")
    if (CutRight=0)
      G_.Call("CutR")
    if (CutUp=0)
      G_.Call("CutU")
    if (CutDown=0)
      G_.Call("CutD")
    GrayDiff:=Round(GrayDiff)
    color:="**" GrayDiff, k:=i:=0
    Loop % nW*nH
      j:=gs[++k]+GrayDiff
		, ascii[k]:=v:=( gs[k-1]>j || gs[k+1]>j
		|| gs[k-nW]>j || gs[k+nW]>j
		|| gs[k-nW-1]>j || gs[k-nW+1]>j
		|| gs[k+nW-1]>j || gs[k+nW+1]>j )
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "AddColorSim", "AddColorDiff":
    _Gui:=FindText_Capture
    c:=_Gui["SelColor"].Value
    if (c="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s12"] " !", 1
      return
    }
    s:=_Gui["ColorList"].Value, c:=StrReplace(c,"0x")
    if InStr(cmd, "Sim")
      v:=_Gui["Sim"].Value, v:=c "@" Round(v/100,2)
		, s:=RegExReplace("/" s, "/" c "@[^/]*") . "/" v
    else
      v:=_Gui["dRGB2"].Value, v:=c "-" Format("{:06X}",v<<16|v<<8|v)
		, s:=RegExReplace("/" s, "/" c "-[^/]*") . "/" v
    _Gui["ColorList"].Value:=Trim(s, "/")
    ControlSend,, {End}, % "ahk_id " _Gui["ColorList"].Hwnd
    G_.Call("Color2Two")
    return
  Case "Undo2":
    _Gui:=FindText_Capture
    s:=_Gui["ColorList"].Value
    s:=RegExReplace("/" s, "/[^/]+$")
    _Gui["ColorList"].Value:=Trim(s, "/")
    ControlSend,, {End}, % "ahk_id " _Gui["ColorList"].Hwnd
    return
  Case "Color2Two":
    _Gui:=FindText_Capture
    color:=Trim(_Gui["ColorList"].Value, "/")
    if (color="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s16"] " !", 1
      return
    }
    ListLines % (lls:=A_ListLines)?0:0
    k:=i:=v:=0, arr:=StrSplit(color, "/")
    Loop % nW*nH
    {
      c:=cors[++k], rr:=(c>>16)&0xFF, gg:=(c>>8)&0xFF, bb:=c&0xFF
      For k1,v1 in arr
      {
        c:=this.ToRGB(StrSplit(StrReplace(v1,"@","-") "-","-")[1])
		, r:=((c>>16)&0xFF)-rr, g:=((c>>8)&0xFF)-gg, b:=(c&0xFF)-bb
        if j:=InStr(v1, "@")
        {
          n:=this.Floor(SubStr(v1,j+1))
		, n:=Floor(4606*255*255*(1-n)*(1-n)), j:=r+rr+rr
          if v:=((1024+j)*r*r+2048*g*g+(1534-j)*b*b<=n)
            Break
        }
        else
        {
          c:=this.Floor("0x" StrSplit(v1 "-","-")[2])
		, dR:=(c>>16)&0xFF, dG:=(c>>8)&0xFF, dB:=c&0xFF
          if v:=(Abs(r)<=dR && Abs(g)<=dG && Abs(b)<=dB)
            Break
        }
      }
      ascii[k]:=v, (show[k] && i:=(v?i+1:i-1))
    }
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "ColorPos2Two":
    _Gui:=FindText_Capture
    c:=_Gui["SelColor"].Value
    if (c="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s12"] " !", 1
      return
    }
    n:=this.Floor(_Gui["Similar2"].Value), n:=Round(n/100,2)
		, color:="#" c "@" n
		, n:=Floor(4606*255*255*(1-n)*(1-n)), k:=i:=0
		, rr:=(c>>16)&0xFF, gg:=(c>>8)&0xFF, bb:=c&0xFF
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nW*nH
      c:=cors[++k], r:=((c>>16)&0xFF)-rr
		, g:=((c>>8)&0xFF)-gg, b:=(c&0xFF)-bb, j:=r+rr+rr
		, ascii[k]:=v:=((1024+j)*r*r+2048*g*g+(1534-j)*b*b<=n)
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "BlackWhite":
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    if (k++)*0 + (++tx)<nW && ty<nH && show[i:=ty*nW+tx+1]
      SendMessage,0x2001,0,(ascii[i] ? 0 : 0xFFFFFF),,% "ahk_id " C_[k]
    return
  Case "Modify":
    Modify:=FindText_Capture["Modify"].Value
    return
  Case "MultiColor":
    MultiColor:=FindText_Capture["MultiColor"].Value
    Result:=""
    ToolTip
    return
  Case "Undo":
    Result:=RegExReplace(Result, ",[^/]+/[^/]+/[^/]+$")
    ToolTip % Trim(Result,"/,")
    return
  Case "GetTxt":
    txt:=""
    if (bg="")
      return
    k:=0
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nH
    {
      v:=""
      Loop % nW
        v.=!show[++k] ? "" : ascii[k] ? "1":"0"
      txt.=v="" ? "" : v "`n"
    }
    ListLines % lls
    return
  Case "Auto":
    G_.Call("GetTxt")
    if (txt="")
    {
      FindText_Capture.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s13"] " !", 1
      return
    }
    While InStr(txt,bg)
    {
      if (txt~="^" bg "+\n")
        txt:=RegExReplace(txt, "^" bg "+\n"), G_.Call("CutU")
      else if !(txt~="m`n)[^\n" bg "]$")
        txt:=RegExReplace(txt, "m`n)" bg "$"), G_.Call("CutR")
      else if (txt~="\n" bg "+\n$")
        txt:=RegExReplace(txt, "\n\K" bg "+\n$"), G_.Call("CutD")
      else if !(txt~="m`n)^[^\n" bg "]")
        txt:=RegExReplace(txt, "m`n)^" bg), G_.Call("CutL")
      else Break
    }
    txt:=""
    return
  Case "OK", "SplitAdd", "AllAdd":
    _Gui:=FindText_Capture
    _Gui.Opt("+OwnDialogs")
    G_.Call("GetTxt")
    if (txt="") && (!MultiColor)
    {
      MsgBox, 4096, Tip, % Lang["s13"] " !", 1
      return
    }
    if InStr(color,"#") && (!MultiColor)
    {
      r:=StrSplit(color,"@","#")
      k:=i:=j:=0
      ListLines % (lls:=A_ListLines)?0:0
      Loop % nW*nH
      {
        if (!show[++k])
          Continue
        i++
        if (k=SelPos)
        {
          j:=i
          Break
        }
      }
      ListLines % lls
      if (j=0)
      {
        MsgBox, 4096, Tip, % Lang["s12"] " !", 1
        return
      }
      color:="#" j "@" r[2]
    }
    Comment:=_Gui["Comment"].Value
    if (cmd="SplitAdd") && (!MultiColor)
    {
      if InStr(color,"#")
      {
        MsgBox, 4096, Tip, % Lang["s14"], 3
        return
      }
      bg:=StrLen(StrReplace(txt,"0"))
        > StrLen(StrReplace(txt,"1")) ? "1":"0"
      s:="", i:=0, k:=nW*nH+1+CutLeft
      Loop % w:=nW-CutLeft-CutRight
      {
        i++
        if (!show[k++] && A_Index<w)
          Continue
        i:=Format("{:d}",i)
        v:=RegExReplace(txt,"m`n)^(.{" i "}).*","$1")
        txt:=RegExReplace(txt,"m`n)^.{" i "}"), i:=0
        While InStr(v,bg)
        {
          if (v~="^" bg "+\n")
            v:=RegExReplace(v,"^" bg "+\n")
          else if !(v~="m`n)[^\n" bg "]$")
            v:=RegExReplace(v,"m`n)" bg "$")
          else if (v~="\n" bg "+\n$")
            v:=RegExReplace(v,"\n\K" bg "+\n$")
          else if !(v~="m`n)^[^\n" bg "]")
            v:=RegExReplace(v,"m`n)^" bg)
          else Break
        }
        if (v!="")
        {
          v:=Format("{:d}",InStr(v,"`n")-1) "." this.bit2base64(v)
          s.="`nText.=""|<" SubStr(Comment,1,1) ">" color "$" v """`n"
          Comment:=SubStr(Comment, 2)
        }
      }
      Event:=cmd, Result:=s
      _Gui.Hide()
      return
    }
    if (!MultiColor)
      txt:=Format("{:d}",InStr(txt,"`n")-1) "." this.bit2base64(txt)
    else
    {
      n:=_Gui["dRGB"].Value
      color:=Format("##{:06X}", n<<16|n<<8|n)
      r:=StrSplit(Trim(StrReplace(Result, ",", "/"), "/"), "/")
		, x:=r[1], y:=r[2], s:="", i:=1
      SetFormat, IntegerFast, d
      Loop % r.Length()//3
        s.="," (r[i++]-x) "/" (r[i++]-y) "/" r[i++]
      txt:=SubStr(s,2)
    }
    s:="`nText.=""|<" Comment ">" color "$" txt """`n"
    if (cmd="AllAdd")
    {
      Event:=cmd, Result:=s
      _Gui.Hide()
      return
    }
    x:=nX+CutLeft+(nW-CutLeft-CutRight)//2
    y:=nY+CutUp+(nH-CutUp-CutDown)//2
    s:=StrReplace(s, "Text.=", "Text:="), r:=StrSplit(Lang["s8"] "|||||||", "|")
    s:="`; #Include <FindText>`n"
    . "`nt1:=A_TickCount, Text:=X:=Y:=""""`n" s
    . "`nif (ok:=FindText(X, Y, " x "-150000, "
    . y "-150000, " x "+150000, " y "+150000, 0, 0, Text))"
    . "`n{"
    . "`n  `; FindText()." . "Click(" . "X, Y, ""L"")"
    . "`n}`n"
    . "`n`; ok:=FindText(X:=""wait"", Y:=3, 0,0,0,0,0,0,Text)    `; " r[7]
    . "`n`; ok:=FindText(X:=""wait0"", Y:=-1, 0,0,0,0,0,0,Text)  `; " r[8]
    . "`n`nMsgBox, 4096, Tip, `% """ r[1] ":``t"" (IsObject(ok)?ok.Length():ok)"
    . "`n  . ""``n``n" r[2] ":``t"" (A_TickCount-t1) "" " r[3] """"
    . "`n  . ""``n``n" r[4] ":``t"" X "", "" Y"
    . "`n  . ""``n``n" r[5] ":``t<"" (IsObject(ok)?ok[1].id:"""") "">""`n"
    . "`nTry For i,v in ok  `; ok " r[6] " ok:=FindText().ok"
    . "`n  if (i<=2)"
    . "`n    FindText().MouseTip(ok[i].x, ok[i].y)`n"
    Event:=cmd, Result:=s
    _Gui.Hide()
    return
  Case "SavePic2":
    x:=nX+CutLeft, w:=nW-CutLeft-CutRight
    y:=nY+CutUp, h:=nH-CutUp-CutDown
    G_.Call("ScreenShot", x "|" y "|" (x+w-1) "|" (y+h-1) "|0")
    return
  Case "ShowPic":
    ControlGet, i, CurrentLine,,, ahk_id %hscr%
    ControlGet, s, Line, %i%,, ahk_id %hscr%
    FindText_Main["MyPic"].Value:=Trim(this.ASCII(s),"`n")
    return
  Case "KeyDown":
    Critical
    _Gui:=FindText_Main
    if (WinExist()!=_Gui.Hwnd)
      return
    Try ctrl:="", ctrl:=args[3]
    if (ctrl=hscr)
      SetTimer, %G_ShowPic%, -150
    else if (ctrl=_Gui["ClipText"].Hwnd)
    {
      s:=_Gui["ClipText"].Value
      _Gui["MyPic"].Value:=Trim(this.ASCII(s),"`n")
    }
    return
  Case "LButtonDown":
    Critical
    Try k1:="", k1:=GuiFromHwnd(args[3],1).Hwnd
    if (k1=FindText_SubPic.Hwnd)
    {
	; Two windows trigger two messages
      if (A_TickCount-oldt)<100 || !this.State("LButton")
        return
      CoordMode, Mouse
      MouseGetPos, k1, k2
      ListLines % (lls:=A_ListLines)?0:0
      Loop
      {
        Sleep 50
        MouseGetPos, k3, k4
        this.RangeTip(Min(k1,k3), Min(k2,k4)
		, Abs(k1-k3)+1, Abs(k2-k4)+1, (A_MSec<500 ? "Red":"Blue"))
      }
      Until !this.State("LButton")
      ListLines % lls
      this.RangeTip()
      this.GetBitsFromScreen(,,,,0,zx,zy)
      this.ClientToScreen(sx, sy, 0, 0, sub_hpic)
      if Abs(k1-k3)+Abs(k2-k4)>4
        sx:=zx+Min(k1,k3)-sx, sy:=zy+Min(k2,k4)-sy
		, sw:=Abs(k1-k3)+1, sh:=Abs(k2-k4)+1
      else
        sx:=zx+k1-sx-71//2, sy:=zy+k2-sy-25//2, sw:=71, sh:=25
      G_.Call("CaptureUpdate")
      FindText_Capture["MyTab1"].Choose(1)
      oldt:=A_TickCount
      return
    }
    else if (k1!=FindText_Capture.Hwnd)
      return G_.Call("KeyDown", arg1, args*)
    MouseGetPos,,,, k2, 2
    k1:=0
    ListLines % (lls:=A_ListLines)?0:0
    For k_,v_ in C_
      if (v_=k2) && (k1:=k_)
        Break
    ListLines % lls
    if (k1<1)
      return
    else if (k1>71*25)
    {
      k3:=nW*nH+dx+(k1-71*25)
      SendMessage,0x2001,0,((show[k3]:=!show[k3])?0x0000FF:0xAAFFFF),,% "ahk_id " k2
      return
    }
    k3:=Mod(k1-1,71)+dx, k4:=(k1-1)//71+dy
    if (k3>=nW || k4>=nH)
      return
    k1:=k, k:=k4*nW+k3+1, k5:=c
    if (MultiColor && show[k])
    {
      c:="," (nX+k3) "/" (nY+k4) "/" Format("{:06X}",cors[k]&0xFFFFFF)
		, Result.=InStr(Result,c) ? "":c
      ToolTip % Trim(Result,"/,")
    }
    if (Modify && bg!="" && show[k])
    {
      c:=((ascii[k]:=!ascii[k]) ? 0 : 0xFFFFFF)
      SendMessage,0x2001,0,c,,% "ahk_id " k2
    }
    else
    {
      c:=cors[k], SelPos:=k
      _Gui:=FindText_Capture
      _Gui["SelGray"].Value:=(((c>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
      _Gui["SelColor"].Value:=Format("0x{:06X}",c&0xFFFFFF)
      _Gui["SelR"].Value:=(c>>16)&0xFF
      _Gui["SelG"].Value:=(c>>8)&0xFF
      _Gui["SelB"].Value:=c&0xFF
    }
    k:=k1, c:=k5
    return
  Case "RButtonDown":
    Critical
    Try k1:="", k1:=GuiFromHwnd(args[3],1).Hwnd
    if (k1!=FindText_SubPic.Hwnd)
      return
	; Two windows trigger two messages
    if (A_TickCount-oldt)<100 || !this.State("RButton")
      return
    r:=[x, y, w, h, pX, pY, pW, pH]
    CoordMode, Mouse
    MouseGetPos, k1, k2
    WinGetPos, x, y, w, h, ahk_id %parent_id%
    WinGetPos, pX, pY, pW, pH, ahk_id %sub_hpic%
    pX-=x, pY-=y, pW-=w, pH-=h
    ListLines % (lls:=A_ListLines)?0:0
    Loop
    {
      Sleep 10
      MouseGetPos, k3, k4
      x:=Min(Max(pX+k3-k1,-pW),0), y:=Min(Max(pY+k4-k2,-pH),0)
      FindText_SubPic.Show("NA x" x " y" y)
      FindText_Capture["MySlider3"].Value:=Round(-x/pW*100)
      FindText_Capture["MySlider4"].Value:=Round(-y/pH*100)
    }
    Until !this.State("RButton")
    ListLines % lls
    x:=r[1], y:=r[2], w:=r[3], h:=r[4], pX:=r[5], pY:=r[6], pW:=r[7], pH:=r[8]
    oldt:=A_TickCount
    return
  Case "MouseMove":
    Try ctrl_name:="", ctrl_name:=GuiCtrlFromHwnd(args[3]).Name
    if (PrevControl != ctrl_name)
    {
      ToolTip
      PrevControl:=ctrl_name
      if (G_ToolTip)
      {
        SetTimer, %G_ToolTip%, % PrevControl ? -500 : "Off"
        SetTimer, %G_ToolTipOff%, % PrevControl ? -5500 : "Off"
      }
    }
    return
  Case "ToolTip":
    MouseGetPos,,, _TT
    if WinExist("ahk_id " _TT " ahk_class AutoHotkeyGUI")
      ToolTip % Tip_Text[PrevControl]
    return
  Case "ToolTipOff":
    ToolTip
    return
  Case "CutL2", "CutR2", "CutU2", "CutD2":
    s:=FindText_Main["MyPic"].Value
    s:=Trim(s,"`n") . "`n", v:=SubStr(cmd,4,1)
    if (v="U")
      s:=RegExReplace(s,"^[^\n]+\n")
    else if (v="D")
      s:=RegExReplace(s,"[^\n]+\n$")
    else if (v="L")
      s:=RegExReplace(s,"m`n)^[^\n]")
    else if (v="R")
      s:=RegExReplace(s,"m`n)[^\n]$")
    FindText_Main["MyPic"].Value:=Trim(s,"`n")
    return
  Case "Update":
    ControlFocus,, % "ahk_id " hscr
    ControlGet, i, CurrentLine,,, ahk_id %hscr%
    ControlGet, s, Line, %i%,, ahk_id %hscr%
    if !RegExMatch(s, "O)(<[^>\n]*>[^$\n]+\$)\d+\.[\w+/]+", r)
      return
    v:=FindText_Main["MyPic"].Value
    v:=Trim(v,"`n") . "`n", w:=Format("{:d}",InStr(v,"`n")-1)
    v:=StrReplace(StrReplace(v,"0","1"),"_","0")
    s:=StrReplace(s, r[0], r[1] . w "." this.bit2base64(v))
    v:="{End}{Shift Down}{Home}{Shift Up}{Del}"
    ControlSend,, %v%, ahk_id %hscr%
    Control, EditPaste, %s%,, ahk_id %hscr%
    ControlSend,, {Home}, ahk_id %hscr%
    return
  }
}

Lang(text:="", getLang:=0)
{
  local
  static init, Lang1, Lang2
  if !VarSetCapacity(init) && (init:="1")
  {
    s:="
    (
Myww       = 宽度 = 调整抓图范围的宽度
Myhh       = 高度 = 调整抓图范围的高度
AddFunc    = 附加 = 复制时带 FindText() 函数
NowHotkey  = 截屏热键 = 当前的截屏热键
SetHotkey1 = = 第一优先级的截屏热键
SetHotkey2 = = 第二优先级的截屏热键
Apply      = 应用 = 应用新的截屏热键
CutU2      = 上删 = 裁剪下面编辑框中文字的上边缘
CutL2      = 左删 = 裁剪下面编辑框中文字的左边缘
CutR2      = 右删 = 裁剪下面编辑框中文字的右边缘
CutD2      = 下删 = 裁剪下面编辑框中文字的下边缘
Update     = 更新 = 更新下面编辑框中文字到代码行中
GetRange   = 获取屏幕范围 = 获取屏幕范围到剪贴板并替换代码中的范围参数
GetOffset  = 获取相对坐标 = 获取相对图像位置的偏移坐标并替换代码中的点击坐标
GetClipOffset  = 获取相对坐标2 = 获取相对左边编辑框的图像的偏移坐标
Capture    = 抓图 = 开始屏幕抓图
CaptureS   = 截屏抓图 = 先截屏,然后显示截屏图像,再手动选择图像内的范围抓图
Test       = 测试 = 测试生成的代码是否可以查找成功
TestClip   = 测试2 = 测试左边文本框中的文字是否可以查找成功,结果复制到剪贴板
Paste      = 粘贴 = 粘贴剪贴板的文字数据
CopyOffset = 复制2 = 复制左边的偏移坐标到剪贴板
Copy       = 复制 = 复制代码到剪贴板
Reset      = 重读 = 重新读取原来的彩色图像
SplitAdd   = 分割添加 = 点击黄色的标签来分割图像为多个图像数据,添加到旧代码中
AllAdd     = 整体添加 = 将文字数据整体添加到旧代码中
Gray2Two      = 灰度阈值二值化 = 灰度小于阈值的为黑色其余白色
GrayDiff2Two  = 灰度差值二值化 = 某点与周围灰度之差大于差值的为黑色其余白色
Color2Two     = 颜色二值化 = 通过颜色列表来转换图像为黑白图
ColorPos2Two  = 颜色位置二值化 = 指定颜色及相似色为黑色其余白色,但是记录该色的位置
SelGray    = 灰度 = 选定颜色的灰度值 (0-255)
SelColor   = 颜色 = 选定颜色的RGB颜色值
SelR       = 红 = 选定颜色的红色分量
SelG       = 绿 = 选定颜色的绿色分量
SelB       = 蓝 = 选定颜色的蓝色分量
RepU       = -上 = 撤销裁剪上边缘1个像素
CutU       = 上 = 裁剪上边缘1个像素
CutU3      = 上3 = 裁剪上边缘3个像素
RepL       = -左 = 撤销裁剪左边缘1个像素
CutL       = 左 = 裁剪左边缘1个像素
CutL3      = 左3 = 裁剪左边缘3个像素
Auto       = 自动 = 二值化之后自动裁剪空白边缘
RepR       = -右 = 撤销裁剪右边缘1个像素
CutR       = 右 = 裁剪右边缘1个像素
CutR3      = 右3 = 裁剪右边缘3个像素
RepD       = -下 = 撤销裁剪下边缘1个像素
CutD       = 下 = 裁剪下边缘1个像素
CutD3      = 下3 = 裁剪下边缘3个像素
Modify     = 修改 = 二值化后可以用鼠标在预览区点击手动修改黑白点
MultiColor = 多色查找 = 鼠标选择多种颜色,之后点击“确定”按钮
Undo       = 撤销 = 撤销上一次选择的颜色
Undo2      = 撤销 = 撤销上一次添加到颜色列表的颜色
Comment    = 识别文字 = 识别文本 (包含在<>中),分割添加时也会分解成单个文字
Threshold  = 灰度阈值 = 灰度阈值 (0-255)
GrayDiff   = 灰度差值 = 灰度差值 (0-255)
Similar1   = 相似度 = 与选定颜色的相似度
Similar2   = 相似度 = 与选定颜色的相似度
AddColorSim  = 添加列表 = 颜色相似模式添加到颜色列表中再运行颜色二值化
AddColorDiff = 添加列表 = 颜色偏色模式添加到颜色列表中再运行颜色二值化
ColorList  = = 颜色列表用于转换图像为二值图
DiffRGB    = 红/绿/蓝 = 多色查找时各分量允许的偏差 (0-255)
DiffRGB2   = 红/绿/蓝 = 多色查找时各分量允许的偏差 (0-255)
Bind0      = 绑定窗口1 = 绑定窗口使用GetDCEx()获取后台窗口图像
Bind1      = 绑定窗口1+ = 绑定窗口使用GetDCEx()并修改窗口透明度
Bind2      = 绑定窗口2 = 绑定窗口使用PrintWindow()获取后台窗口图像
Bind3      = 绑定窗口2+ = 绑定窗口使用PrintWindow()并修改窗口透明度
Bind4      = 绑定窗口3 = 绑定窗口使用PrintWindow(,,3)获取后台窗口图像
OK         = 确定 = 生成全新的代码替换旧代码
OK2        = 确定 = 恢复截屏到屏幕然后再抓图
Cancel     = 取消 = 关闭窗口不做任何事
Cancel2    = 取消 = 关闭窗口不做任何事
ClearAll   = 清空 = 清空所有保存的截图
OpenDir    = 打开目录 = 打开保存屏幕截图的目录
SavePic    = 保存图片 = 选择一个范围保存为图片
SavePic2   = 保存图片 = 将修剪后的原始图像保存为图片
LoadPic    = 载入图片 = 载入一张图片作为抓取的图像
ClipText   = = 显示粘贴的文字数据
Offset     = = 显示“获取相对坐标2”或者“获取屏幕范围”的结果
SelectBox  = = 选择截图显示到屏幕左上角
s1  = FindText
s2  = 灰度阈值|灰度差值|颜色|颜色位置|多色查找
s3  = 图像二值化及分割
s4  = 抓图生成字库及找字代码
s5  = 方向键微调选框\n先点击右键(Ctrl)一次\n把鼠标移开\n再点击右键(Ctrl)一次
s6  = 解绑窗口使用
s7  = 左键(Ctrl)拖动选择范围\n坐标复制到剪贴板
s8  = 找到|时间|毫秒|位置|结果|值可以这样获取|等待3秒等图像出现|无限等待等图像消失
s9  = 截屏成功
s10 = 鼠标位置|穿透显示绑定窗口\n点击右键完成抓图
s11 = 请先设定灰度差值
s12 = 请先选择核心颜色
s13 = 请先将图像二值化
s14 = 不能用于颜色位置二值化模式, 因为分割后会导致位置错误
s15 = 你确定选择的范围吗?\n\n如果不确定,可以重新选择
s16 = 请先添加颜色到颜色列表
s17 = 请先保存图片
s18 = 捕获|截图
	)"
	Lang1:=[], Lang2:=[]
	loop Parse, s, `n, `r
		if InStr(v:=A_LoopField, "=")
			r:=StrSplit(StrReplace(v "==","\n","`n"), "=", "`t ")
			, Lang1[r[1]]:=r[2], Lang2[r[1]]:=r[3]
		}
return getLang=1 ? Lang1 : getLang=2 ? Lang2 : Lang1[text]
}

}  ;// Class End


;---------------------------------
; Gui-V1-V2 兼容库  By FeiYue
;---------------------------------

Gui(args*) {
	return new GuiCreate(args*)
}

GuiFromHwnd(hwnd:="AllGuiObj", RecurseParent:=0) {
	static init, AllGuiObj
	if !VarSetCapacity(init) && (init:="1")
		AllGuiObj:=[]
	if (hwnd=="AllGuiObj")
		return AllGuiObj
	if (RecurseParent)
		while hwnd && !AllGuiObj.HasKey(hwnd)
			hwnd:=DllCall("GetParent", "Ptr",hwnd, "Ptr")
	return AllGuiObj[hwnd]
}

GuiCtrlFromHwnd(hwnd) {
	return GuiFromHwnd(hwnd,1)[hwnd]
}

GuiCreate_Close(args*) {
	return GuiCreate_G("Close", args*)
}

GuiCreate_ContextMenu(args*) {
	return GuiCreate_G("ContextMenu", args*)
}

GuiCreate_DropFiles(args*) {
	return GuiCreate_G("DropFiles", args*)
}

GuiCreate_Escape(args*) {
	return GuiCreate_G("Escape", args*)
}

GuiCreate_Size(args*) {
	return GuiCreate_G("Size", args*)
}

GuiCreate_G(EventName, args*) {
	local
	return (G:=GuiFromHwnd(WinExist()))["_" EventName].Call(G, args*)
}

class GuiCreate {

	__New(opts:="", title:="", args*) {
local
Gui, New, % opts " +Hwndhwnd +LabelGuiCreate_", % title
this.Hwnd:=hwnd, this.ClassNN:=[]
GuiFromHwnd()[hwnd]:=this
}

__Delete() {
	this.Destroy()
}

Destroy() {
	local
	if !(hwnd:=this.Hwnd)
		return
	this.Hwnd:="", GuiFromHwnd().Delete(hwnd)
	Try Gui, % hwnd ":Destroy"
		For k,v in this
		(v.Hwnd && v.Hwnd:=""), this[k]:=""
  }

  OnEvent(EventName, Callback, AddRemove:=1) {
    if IsObject(Callback)
      this["_" EventName]:=Callback
  }

  Opt(opts) {
    Gui, % this.Hwnd ":" RegExReplace(opts,"i)[+\-\s]Label\S*")
  }

  Add(type, opts:="", text:="") {
    local
    static init, type2class
    if !VarSetCapacity(init) && (init:="1")
      type2class:=[]
    type:=(type="DropDownList"?"DDL":type="Picture"?"Pic":type)
    name:=RegExMatch(opts, "i)(^|[+\-\s])V(?!Scroll\b|ertical\b)\K\S*", r)?r:""
    opts:=RegExReplace(opts, "i)(^|[+\-\s])V(?!Scroll\b|ertical\b)\S*")
    if IsObject(text)
    {
      s:=""
      For k,v in text
        s.="|" v
      text:=Trim(s, "|")
    }
    Gui, % this.Hwnd ":Add", % type, % opts " +Hwndhwnd", % text
    this.LastHwnd:=hwnd
    if type2class.HasKey(type)
      s:=type2class[type]
    else
    {
      WinGetClass, s, ahk_id %hwnd%
      type2class[type]:=s
    }
    this.ClassNN[s]:=n:=Floor(this.ClassNN[s])+1, classnn:=s . n
    obj:= new this.Control(this.Hwnd, hwnd, type, classnn, name)
    this[hwnd]:=obj, this[classnn]:=obj
    if (name) && !(name~="i)^(Destroy|OnEvent|Opt|Add"
    . "|SetFont|Show|Hide|Move|GetClientPos|GetPos|Maximize"
    . "|Minimize|Restore|Flash|Submit|Hwnd|Name|Title"
    . "|BackColor|MarginX|MarginY|MenuBar|FocusedCtrl)$")
      this[name]:=obj
    return obj
  }

  SetFont(opts:="", FontName:="") {
    Gui, % this.Hwnd ":Font", % opts, % FontName
  }

  Show(opts:="", args*) {
    Gui, % this.Hwnd ":Show", % opts
  }

  Hide() {
    Gui, % this.Hwnd ":Hide"
  }

  Move(x:="", y:="", w:="", h:="") {
    local
    this.GetPos(pX, pY, pW, pH)
    x:=(x=""?pX:x), y:=(y=""?pY:y), w:=(w=""?pW:w), h:=(h=""?pH:h)
    DllCall("MoveWindow", "Ptr",this.Hwnd, "int",x, "int",y, "int",w, "int",h, "int",1)
  }

  GetClientPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    VarSetCapacity(rect, 16, 0)
				, DllCall("GetClientRect",  "Ptr",this.Hwnd, "Ptr",&rect)
				, DllCall("ClientToScreen", "Ptr",this.Hwnd, "Ptr",&rect)
				, x:=NumGet(rect, 0, "int"), y:=NumGet(rect, 4, "int")
				, w:=NumGet(rect, 8, "int")-x, h:=NumGet(rect, 12, "int")-y
  }

  GetPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    VarSetCapacity(rect, 16, 0)
				, DllCall("GetWindowRect",  "Ptr",this.Hwnd, "Ptr",&rect)
				, x:=NumGet(rect, 0, "int"), y:=NumGet(rect, 4, "int")
				, w:=NumGet(rect, 8, "int")-x, h:=NumGet(rect, 12, "int")-y
  }

  Maximize() {
    Gui, % this.Hwnd ":Maximize"
  }

  Minimize() {
    Gui, % this.Hwnd ":Minimize"
  }

  Restore() {
    Gui, % this.Hwnd ":Restore"
  }

  Flash(k:=1) {
    Gui, % this.Hwnd ":Flash", % k ? "":"Off"
  }

  Submit(hide:=1) {
    local
    (hide && this.Hide()), arr:=[]
    For k,v in this
      if k is number
        if (v.Name!="")
          arr[v.Name]:=v.Value
    return arr
  }

  BackColor {
    get {
      return this._BackColor
    }
    set {
      this._BackColor:=value
      Gui, % this.Hwnd ":Color", % value
      return value
    }
  }

  MarginX {
    get {
      return this._MarginX
    }
    set {
      this._MarginX:=value
      Gui, % this.Hwnd ":Margin", % value
      return value
    }
  }

  MarginY {
    get {
      return this._MarginY
    }
    set {
      this._MarginY:=value
      Gui, % this.Hwnd ":Margin",, % value
      return value
    }
  }

  MenuBar {
    get {
      return this._MenuBar
    }
    set {
      this._MenuBar:=value
      Gui, % this.Hwnd ":Menu", % value
      return value
    }
  }

  Title {
    get {
      local
      VarSetCapacity(v, 260*2)
      DllCall("GetWindowText", "Ptr",this.Hwnd, "Str",v, "Int",260)
      return v
    }
    set {
      DllCall("SetWindowText", "Ptr",this.Hwnd, "Str",value)
      return value
    }
  }

  FocusedCtrl {
    get {
      local
      GuiControlGet, v, % this.Hwnd ":Focus"
      return this[v]
    }
  }

			;========  Sub Class =========

  Class Control {

  __New(GuiHwnd, hwnd, type, classnn, name) {
    this.GuiHwnd:=GuiHwnd, this.Hwnd:=hwnd
    this.Type:=type, this.ClassNN:=classnn, this.Name:=name
  }

  Opt(opts) {
    GuiControl, % opts, % this.Hwnd
  }

  OnEvent(EventName, Callback, AddRemove:=1) {
    local
    r:=this.OnEvent_G.Bind(this, Callback)
    GuiControl, +g, % this.Hwnd, % r
  }

  OnEvent_G(Callback, args*) {
    if IsObject(Callback)
      return %Callback%(this, args*)
  }

  GetPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    GuiControlGet, p, Pos, % this.Hwnd
    x:=Floor(pX), y:=Floor(pY), w:=Floor(pW), h:=Floor(pH)
  }

  Move(x:="", y:="", w:="", h:="") {
    local
    s:=(x=""?"":" x" x) (y=""?"":" y" y) (w=""?"":" w" w) (h=""?"":" h" h)
    GuiControl, Move, % this.Hwnd, % s
  }

  Redraw() {
    GuiControl, MoveDraw, % this.Hwnd
  }

  Focus() {
    GuiControl, Focus, % this.Hwnd
  }

  UseTab(Name:="", Exact:="", index:="") {
    Gui, % this.GuiHwnd ":Tab", % Name, % index, % Exact?"Exact":""
  }

  SetFont(opts:="", FontName:="") {
    Gui, % this.GuiHwnd ":Font", % opts, % FontName
    GuiControl, Font, % this.Hwnd
  }

  Add(text) {
    local
    if IsObject(text)
    {
      s:=""
      For k,v in text
        s.="|" v
      text:=Trim(s, "|")
    }
    GuiControl,, % this.Hwnd, % text
  }

  Delete(N:="") {
    if (N="")
      GuiControl,, % this.Hwnd, |
    else
      this.Choose(N), this.Choose(0)
  }

  Choose(N) {
    if N is number
      GuiControl, Choose, % this.Hwnd, % N
    else
      GuiControl, ChooseString, % this.Hwnd, % N
  }

  Gui {
    get {
      return GuiFromHwnd(this.GuiHwnd)
    }
  }

  Enabled {
    get {
      local
      GuiControlGet, v, Enabled, % this.Hwnd
      return v
    }
    set {
      GuiControl, % "Enable" (!!value), % this.Hwnd
      return value
    }
  }

  Visible {
    get {
      local
      GuiControlGet, v, Visible, % this.Hwnd
      return v
    }
    set {
      GuiControl, % "Show" (!!value), % this.Hwnd
      return value
    }
  }

  Focused {
    get {
      local
      GuiControlGet, v, % this.GuiHwnd ":Focus"
      return (v=this.ClassNN)
    }
  }

  Value {
    get {
      local
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        this.Opt("+AltSubmit")
      GuiControlGet, v,, % this.Hwnd
      return v
    }
    set {
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        GuiControl, Choose, % this.Hwnd, % value
      else
        GuiControl,, % this.Hwnd, % value
      return value
    }
  }

  Text {
    get {
      local
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        this.Opt("-AltSubmit")
      GuiControlGet, v,, % this.Hwnd
      return v
    }
    set {
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        GuiControl, ChooseString, % this.Hwnd, % value
      else
        GuiControl,, % this.Hwnd, % value
      return value
    }
  }

}
}    ;==> Class End


			;================= The End =================

			;

;
;  https://www.cnblogs.com/delphixx/p/18374739
;


  免输密码全自动登录金山文档Windows客户端 2024年8月22日

免输密码全自动登录有道云笔记Windows客户端 2024年8月22日

; 免输密码全自动登录金山文档Windows客户端 2024年8月22日
;
; 指纹加密U盘&FindText-v9.7-by-FeiYue&Loop-if-break&金山文档&Index-Your-Files&mstsc&零层壹号&WinSCP&USMv5&Acronis-true-Image-2021-WinPE&GoodSync&向日葵控控A2
;
; Index-Your-Files 能够索引DVD-ROM只读光盘虚拟光驱量产U盘,MasterSeeker、Everything、Quick Search、UltraSearch和Listary无法索引DVD-ROM只读光盘虚拟光驱量产U盘并自定义指定索引数据库文件存放的位置。
;
; 常见的指纹加密U盘有 联想FU100、联想FU200、移速双接口御存系列、亿捷指纹加密U盘、毕亚兹指纹加密U盘等。
;
;  不同的显示器分辨率和字体缩放显示比例会造成图像变形从而使FindText()函数无法找到相对应的灰度二值化图片,
;  此脚本在不同电脑之间无法通用,如果各位观众网友有好的解决方法希望能在评论区分享一下,谢谢。

; 设置此脚本的运行参数
#SingleInstance force
#Persistent
#WinActivateForce
DetectHiddenWindows,On
SetTitleMatchMode,2

; 关闭并重新运行金山文档
WinClose,登录 ahk_exe 金山文档.exe
Process,Close,金山文档.exe
Run,"C:\Program Files (x86)\金山文档\金山文档.exe"

; 等待出现并点击账号密码的锁形按钮,注意请使用FindText的截屏抓图功能截图,灰度阈值我填222

Text:="|<>*222$24.000003k00Dw00yT00s701k3U1U1U3U1k300k300k7zzsDzzwQ00CM006M006M006M006M006M1k6M3k6M3U6M006M006M006M006M006Q00CDzzw7zzs0000U"
loop,100
{
	if (ok:=FindText(X, Y, 1850-150000, 1238-150000, 1850+150000, 1238+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待出现并点击同意按钮

Text:="|<>*222$41.zzzzzzzzzzzwzz003zxzyzzrU01xzzjvzDvzzTtwzo02s007jzxzzzzTzvs01ys1rrzvxrvjU07vjrTTzjrTiy00TizRxxyzQ0vqNyyvzrAtgxzziNzQvzzRs1zrzUzzzzzzzzzzy"
loop,100
{
	if (ok:=FindText(X, Y, 1762-150000, 1014-150000, 1762+150000, 1014+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待出现并点击手机号输入框
Text:="|<>*222$43.00000000k000000M00A1U0A7y60k06327zzszxX6Em0NalaAFkAnNXP0M6NgkADz3AqE661VaPQTv0kzxb1VzsNalUkkAAnMMyM66NgATg33Aq6STzVaP3/30knBVhVUMTyzYkkAA3M0MTy00A0AA3006000000000001"
loop,100
{
	if (ok:=FindText(X, Y, 2067-150000, 550-150000, 2067+150000, 550+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

Sleep,1000

; 等待出现并输入手机号和登录密码
#IfWinActive,登录 ahk_exe 金山文档.exe
; 这里需要更换成你自己的手机号
Send,138####5488
Sleep,100
Send,{tab}
Sleep,100
; 这里需要更换成你自己的金山文档的登录密码
Send,ba??.l??3##4794
#IfWinActive

; 等待出现并点击智能验证的跳动圆点

Text:="|<>*222$51.000000000000zs000000zzs00000zzzs0000Tzzzk0007zzzz0003zzzzy000zzzzzs00DzzzzzU03zzzzzy00zzzzzzs07zzzzzz01zzzzzzw0Tzzzzzzk3zzzzzzy0zzzsTzzs7zzw0zzz1zzk00DzwDzy001zzVzzk00DzwDzy001zzXzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyTzy001zznzzk00DzyDzz001zzVzzs00TzwDzzU07zzVzzw00zzw7zzk0Dzz0zzzU7zzs3zzz3zzy0Tzzzzzzk1zzzzzzw07zzzzzz00zzzzzzs03zzzzzy00DzzzzzU00zzzzzs003zzzzy0007zzzz0000Tzzzk0000zzzs00001zzw000000zs000000000004"
loop,100
{
	if (ok:=FindText(X, Y, 1990-150000, 774-150000, 1990+150000, 774+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 等待验证成功
Sleep,3000

; 等待出现并点击立即登录按钮
Text:="|<>*222$86.zzzzzzzzzzzzzzzzTzzzzzzzzzzzzznzzzzzzwxzzzzzyTz0A0s1AzU03zzjzrvDDysTzzwz000RynnjCTTzzDzzzzTgwlbnDU03zjzjk3DD3y7zzwztznxynnt0HzzzDyTwzTgwwzzC000TnzDrvDAzzwzxzzwzbw0nnM03yzTrzDtzTgwyzyzrntztyzrzDDjzjyQwzyTDxnnnvzvzr6TzbnzTQwy00zzIDzwtzrXDDXyjzZbzzSTxknryTbzbQzzzDz1o1zrnzbrnw000VxDzwxznxz7zzzxznz000/kTzzzzzzzzzzzzzzzs"
loop,100
{
	if (ok:=FindText(X, Y, 2188-150000, 943-150000, 2188+150000, 943+150000, 0, 0, Text))
	{
		FindText().Click(X, Y, "L")
		break
	}
	else
	{
		Sleep,100
	}
}

; 登录成功时关闭此脚本,终止此脚本
ExitApp


;==========



FindText(ByRef x:="FindTextClass", ByRef y:="", args*)
{
	static init, obj
	if !VarSetCapacity(init) && (init:="1")
		obj:=new FindTextClass()
	return (x=="FindTextClass" && !args.Length()) ? obj : obj.FindText(x, y, args*)
}

class FindTextClass
{  ;// Class Begin

	Floor(i)
	{
		if i is number
			return i+0
		else return 0
	}

	__New()
{
	this.bits:={ Scan0: 0, hBM: 0, oldzw: 0, oldzh: 0 }
	this.bind:={ id: 0, mode: 0, oldStyle: 0 }
	this.Lib:=[]
	this.Cursor:=0
}

__Delete()
{
	if (this.bits.hBM)
		Try DllCall("DeleteObject", "Ptr",this.bits.hBM)
}

New()
{
	return new FindTextClass()
}

help()
{
	return "
	(
		;--------------------------------
		;  FindText - 屏幕找字函数
		;  版本 : 9.7  (2024-06-18)
		;--------------------------------
		;  返回变量:=FindText(
		;      OutputX --> 保存返回的X坐标的变量名称
		;    , OutputY --> 保存返回的Y坐标的变量名称
		;    , X1 --> 查找范围的左上角X坐标
		;    , Y1 --> 查找范围的左上角Y坐标
		;    , X2 --> 查找范围的右下角X坐标
		;    , Y2 --> 查找范围的右下角Y坐标
		;    , err1 --> 文字的黑点容错百分率(0.1=10%)
		;    , err0 --> 背景的白点容错百分率(0.1=10%)
		;    , Text --> 由工具生成的查找图像的数据,可以一次查找多个,用“|”分隔
		;    , ScreenShot --> 是否截屏,为0则使用上一次的截屏数据
		;    , FindAll --> 是否搜索所有位置,为0则找到一个位置就返回
		;    , JoinText --> 如果想组合查找,可以为1,或者是要查找单词的数组
		;    , offsetX --> 组合图像的每个字和前一个字的最大横向间隔
		;    , offsetY --> 组合图像的每个字和前一个字的最大高低间隔
		;    , dir --> 查找的方向,有上、下、左、右、中心9种
		;    , zoomW --> 图像宽度的缩放百分率(1.0=100%)
		;    , zoomH --> 图像高度的缩放百分率(1.0=100%)
		;  )
		;
		;  返回变量 --> 如果没找到结果会返回0。否则返回一个二级数组,
		;      第一级是每个结果对象,第二级是结果对象的具体信息对象:
		;      { 1:左上角X, 2:左上角Y, 3:图像宽度W, 4:图像高度H
		;        , x:中心点X, y:中心点Y, id:图像识别文本 }
		;  坐标都是相对于屏幕,颜色使用RGB格式
		;
		;  如果 OutputX 等于 'wait' 或 'wait1' 意味着等待图像出现,
		;  如果 OutputX 等于 'wait0' 意味着等待图像消失
		;  此时 OutputY 设置等待时间的秒数,如果小于0则无限等待
		;  如果超时则返回0,意味着失败,如果等待图像出现成功,则返回位置数组
		;  如果等待图像消失成功,则返回 1
		;  例1: FindText(X:='wait', Y:=3, 0,0,0,0,0,0,Text)   ; 等待3秒等图像出现
		;  例2: FindText(X:='wait0', Y:=-1, 0,0,0,0,0,0,Text) ; 无限等待等图像消失
		;
		;  <FindMultiColor> 或 <FindColor> : 找色 是仅有一个点的 多点找色
		;  Text:='|<>##DRDGDB $ 0/0/RRGGBB1-DRDGDB1/RRGGBB2, xn/yn/-RRGGBB3/RRGGBB4, ...'
		;  '##'之后的颜色 (0xDRDGDB) 是所有颜色的默认偏色(各个分量允许的变化值)
		;  初始点 (0,0) 匹配 0xRRGGBB1(+/-0xDRDGDB1) 或者 0xRRGGBB2(+/-0xDRDGDB),
		;  点 (xn,yn) 匹配 排除 0xRRGGBB3(+/-0xDRDGDB) 和排除 0xRRGGBB4(+/-0xDRDGDB)
		;  点坐标后面以 '-' 开头表示要排除后面的所有颜色,其他颜色都匹配
		;  每个点最多允许匹配10组颜色 (xn/yn/RRGGBB1/.../RRGGBB10)
		;
		;  <FindPic> : Text 参数需要手动输入
		;  Text:='|<>##DRDGDB-RRGGBB1-RRGGBB2... $ d:\a.bmp'
		;  0xRRGGBB1(+/-0xDRDGDB)... 都是透明色,不参与匹配
		;
		;--------------------------------
	)"
}

FindText(ByRef OutputX:="", ByRef OutputY:=""
	, x1:=0, y1:=0, x2:=0, y2:=0, err1:=0, err0:=0, text:=""
	, ScreenShot:=1, FindAll:=1, JoinText:=0, offsetX:=20, offsetY:=10
	, dir:=1, zoomW:=1, zoomH:=1)
{
	local
	if (OutputX ~= "i)^\s*wait[10]?\s*$")
	{
		found:=!InStr(OutputX,"0"), time:=this.Floor(OutputY)
			, timeout:=A_TickCount+Round(time*1000), OutputX:=""
		loop
		{
			ok:=this.FindText(,, x1, y1, x2, y2, err1, err0, text, ScreenShot
				, FindAll, JoinText, offsetX, offsetY, dir, zoomW, zoomH)
			if (found && ok)
			{
				OutputX:=ok[1].x, OutputY:=ok[1].y
				return ok
			}
			if (!found && !ok)
				return 1
			if (time>=0 && A_TickCount>=timeout)
				break
			Sleep 50
		}
		return 0
	}
	SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
	x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
	if (x1=0 && y1=0 && x2=0 && y2=0)
		n:=150000, x:=y:=-n, w:=h:=2*n
	else
		x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
	bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
		, this.ok:=0, info:=[]
	loop Parse, text, |
		if IsObject(j:=this.PicInfo(A_LoopField))
			info.Push(j)
	if (w<1 || h<1 || !(num:=info.Length()) || !bits.Scan0)
	{
		SetBatchLines % bch
		return 0
	}
	arr:=[], info2:=[], k:=0, s:=""
		, mode:=(IsObject(JoinText) ? 2 : JoinText ? 1 : 0)
	For i,j in info
	{
		k:=Max(k, (j[7]=5 && j[8]=0 ? j[9] : j[2]*j[3]))
		if (mode)
			v:=(mode=1 ? i : j[10]) . "", (mode=1 && s.="|" v)
			, (!info2.HasKey(v) && info2[v]:=[]), (v!="" && info2[v].Push(j))
	}
	sx:=x, sy:=y, sw:=w, sh:=h
		, (mode=1 && JoinText:=[s])
		, VarSetCapacity(s1,k*4), VarSetCapacity(s0,k*4), VarSetCapacity(ss,sw*(sh+3))
		, allpos_max:=(FindAll || JoinText ? 10240 : 1)
		, ini:={ sx:sx, sy:sy, sw:sw, sh:sh, zx:zx, zy:zy
		, mode:mode, bits:bits, ss:&ss, s1:&s1, s0:&s0
		, err1:err1, err0:err0, allpos_max:allpos_max
		, zoomW:zoomW, zoomH:zoomH }
	loop 2
	{
		if (err1=0 && err0=0) && (num>1 || A_Index>1)
			ini.err1:=err1:=0.05, ini.err0:=err0:=0.05
		if (!JoinText)
		{
			VarSetCapacity(allpos, allpos_max*4), allpos_ptr:=&allpos
			For i,j in info
				loop % this.PicFind(ini, j, dir, sx, sy, sw, sh, allpos_ptr)
				{
					pos:=NumGet(allpos, 4*(A_Index-1), "uint")
						, x:=(pos&0xFFFF)+zx, y:=(pos>>16)+zy
						, w:=Floor(j[2]*zoomW), h:=Floor(j[3]*zoomH), comment:=j[10]
						, arr.Push({1:x, 2:y, 3:w, 4:h, x:x+w//2, y:y+h//2, id:comment})
					if (!FindAll)
						break 3
				}
		}
		else
			For k,v in JoinText
			{
				v:=StrSplit(Trim(RegExReplace(v, "\s*\|[|\s]*", "|"), "|")
					, (InStr(v,"|")?"|":""), " `t")
					, this.JoinText(arr, ini, info2, v, 1, offsetX, offsetY
					, FindAll, dir, 0, 0, 0, sx, sy, sw, sh)
				if (!FindAll && arr.Length())
					break 2
			}
		if (err1!=0 || err0!=0 || arr.Length() || info[1][4] || info[1][7]=5)
			break
	}
	SetBatchLines % bch
	if (arr.Length())
	{
		OutputX:=arr[1].x, OutputY:=arr[1].y, this.ok:=arr
		return arr
	}
	return 0
}

; 组合文本参数可以用数组 <==> [ "abc", "xyz", "a1|a2|a3" ]

JoinText(arr, ini, info2, text, index, offsetX, offsetY
	, FindAll, dir, minX, minY, maxY, sx, sy, sw, sh)
{
	local
	if !(Len:=text.Length()) || !info2.HasKey(key:=text[index])
		return 0
	VarSetCapacity(allpos, ini.allpos_max*4), allpos_ptr:=&allpos
		, zoomW:=ini.zoomW, zoomH:=ini.zoomH, mode:=ini.mode
	For i,j in info2[key]
		if (mode!=2 || key==j[10])
			loop % this.PicFind(ini, j, dir, sx, sy, (index=1 ? sw
: Min(sx+offsetX+Floor(j[2]*zoomW),ini.sx+ini.sw)-sx), sh, allpos_ptr)
	{
		pos:=NumGet(allpos, 4*(A_Index-1), "uint")
			, x:=pos&0xFFFF, y:=pos>>16
			, w:=Floor(j[2]*zoomW), h:=Floor(j[3]*zoomH)
			, (index=1 && (minX:=x, minY:=y, maxY:=y+h))
			, minY1:=Min(y, minY), maxY1:=Max(y+h, maxY), sx1:=x+w
		if (index<Len)
		{
			sy1:=Max(minY1-offsetY, ini.sy)
				, sh1:=Min(maxY1+offsetY, ini.sy+ini.sh)-sy1
			if this.JoinText(arr, ini, info2, text, index+1, offsetX, offsetY
				, FindAll, 5, minX, minY1, maxY1, sx1, sy1, 0, sh1)
				&& (index>1 || !FindAll)
				return 1
		}
		else
		{
			comment:=""
			For k,v in text
				comment.=(mode=2 ? v : info2[v][1][10])
			x:=minX+ini.zx, y:=minY1+ini.zy, w:=sx1-minX, h:=maxY1-minY1
				, arr.Push({1:x, 2:y, 3:w, 4:h, x:x+w//2, y:y+h//2, id:comment})
			if (index>1 || !FindAll)
				return 1
		}
	}
return 0
}

PicFind(ini, j, dir, sx, sy, sw, sh, allpos_ptr)
{
	local
	static init, MyFunc
	if !VarSetCapacity(init) && (init:="1")
	{
		x32:="VVdWU4HskAAAAIuEJKQAAACLvCSoAAAAi6wk3AAAAMcEJAAAAACD6AGD+AQPh48F"
		. "AACDvCSkAAAABQ+EkQUAAIuEJOAAAACFwA+ONA0AADHAibwkqAAAAMcEJAAAAADH"
		. "RCQUAAAAAMdEJAwAAAAAicfHRCQYAAAAAI20JgAAAACLhCTYAAAAi0wkGDH2MdsB"
		. "yIXtiUQkCH896ZAAAABmkA+vhCTEAAAAicGJ8Jn3@QHBi0QkCIA8GDF0TIuEJNQA"
		. "AACDwwEDtCT4AAAAiQy4g8cBOd10VIsEJJn3vCTgAAAAg7wkpAAAAAR1tQ+vhCS4"
		. "AAAAicGJ8Jn3@Y0MgYtEJAiAPBgxdbSLRCQMi5Qk0AAAAIPDAQO0JPgAAACJDIKD"
		. "wAE53YlEJAx1rAFsJBiDRCQUAYuMJPwAAACLRCQUAQwkOYQk4AAAAA+FMv@@@4tM"
		. "JAy7rYvbaIn+D6+MJOQAAACJfCQ0i7wkqAAAAInIwfkf9+vB+gwpyouMJOgAAACJ"
		. "VCRID6@OicjB+R@368H6DCnKiVQkTIO8JKQAAAAED4TGCAAAi4QkzAAAAAOEJMQA"
		. "AACLtCS8AAAAi4wkuAAAAIlEJCiLhCS4AAAAD6+EJMAAAACNNLCLhCTEAAAA99iD"
		. "vCSkAAAAAY0EgYlEJDgPhKAJAACDvCSkAAAAAg+EIgcAAIuUJMgAAACF0g+O9wcA"
		. "AIuMJMQAAACLRCQoifXHBCQAAAAAx0QkCAAAAACJvCSoAAAAjQRIiUQkHInIweAC"
		. "iUQkFIuEJMQAAACFwH5ai4wktAAAAItcJByLvCS0AAAAA1wkCAHpA2wkFAHvjXYA"
		. "D7ZRAoPBBIPDAWvyJg+2Uf1rwkuNFAYPtnH8ifDB4AQp8AHQwfgHiEP@Ofl10ou0"
		. "JMQAAAABdCQIgwQkAQNsJDiLBCQ5hCTIAAAAdYeLhCTEAAAAi5QkrAAAADHtMfaD"
		. "6AGJRCQUi4QkyAAAAIPoAYlEJBiLhCTEAAAAhcAPjuIAAACLvCTEAAAAi0QkHAH3"
		. "jQwwifuJfCQgiccB2InzK5wkxAAAAIkEJDHAAfuLfCQoAfeLNCSJHCSJfCQIjXYA"
		. "hcAPhJQQAAA5RCQUD4SKEAAAhe0PhIIQAAA5bCQYD4R4EAAAD7YRD7Z5@7sBAAAA"
		. "A5QkqAAAADn6ckUPtnkBOfpyPYs8JA+2Pzn6cjMPtj45+nIsizwkD7Z@@zn6ciGL"
		. "PCQPtn8BOfpyFg+2fv85+nIOD7ZeATnaD5LDkI10JgCLfCQIiBwHg8ABg8EBg8YB"
		. "gwQkATmEJMQAAAAPhV@@@@+LdCQgg8UBOawkyAAAAA+F@@7@@4u8JKgAAACJlCSs"
		. "AAAAi4QkxAAAAMdEJBQAAAAAx0QkGAAAAACJvCSoAAAAg+gBiUQkCJCNtCYAAAAA"
		. "i4QkxAAAAIXAD468BQAAi0QkGItcJCgxyYuUJMwAAAC@AwAAAAHDAdAPtjOJBCTr"
		. "aY1W@7gCAAAAg@oBdhOD@wEPlMKD@QEPlMAJ0A+2wAHAgeb9AAAAugEAAAB0EoX@"
		. "D5TChe2J1w+UwonWCfeJ+os8JAnQiAQPg8EBOYwkxAAAAA+EOAUAAIXJD7ZzAQ+E"
		. "PA8AAA+2O4PDATtMJAgPhFkOAAAPtmsBi4Qk7AAAAIXAD4V6@@@@jUb@g@gCGcCD"
		. "4AKB5v0AAAAPlMLro4t0JCiLbCQsx0QkbAAAAADHRCRoAQAAAMdEJEQAAAAAx0Qk"
		. "YAAAAACJ8I1eAY1VAcHoH4lsJDjHBCQAAAAAAfDR+IlEJBiJ6MHoHwHo0fiJRCQc"
		. "ifCJ3g+v8o1ICYl0JHyJxonog8AJOdOJdCQ8D0@Bi3QkfInBD6@IOXQkbImMJIAA"
		. "AAB9TYu0JIAAAAA5dCRgx0QkZAAAAAB9OItMJGg5TCRkD4wRCAAAi3QkRINEJGAB"
		. "ifCD4AEBwYnwi3QkfIPAAYlMJGiD4AM5dCRsiUQkRHyziwQkgcSQAAAAW15fXcJc"
		. "ADHAhf8PlcCJRCRcD4TNBAAAi4Qk4AAAAIu0JNgAAACJ+cHpEIn7D7bJD7bTD6@F"
		. "jQSGic4Pr@GJRCQkifgPtsSJdCQwicYPr@CJ0A+vwol0JASJRCQQi4Qk4AAAAIXA"
		. "D466CAAAi3QkJI0ErQAAAACJ+4msJNwAAACLvCSsAAAAi2wkMIlEJDwxwMdEJCwA"
		. "AAAAx0QkNAAAAADHRCQMAAAAAIk0JIu0JNwAAACF9g+OGAEAAIuMJNgAAACLNCTH"
		. "RCQYAAAAAIlcJCABwQNEJDyJTCQUiUQkOAOEJNgAAACJRCQoi0QkFIX@D7ZQAQ+2"
		. "SAIPtgCJFCSJRCQIdEQx0osclonYwegQD7bAKcgPr8A5xXwjD7bHKwQkD6@AOUQk"
		. "BHwUD7bDK0QkCA+vwDlEJBAPjRkDAACDwgE5+nXCiVwkIItEJAzB4RDB4AKJRCQc"
		. "i0QkLJn3vCTgAAAAD6+EJLgAAACJw4tEJBiZ97wk3AAAAItUJAyNBIOLnCTQAAAA"
		. "iQSTiwQkg8IBi5wk1AAAAIlUJAzB4AgJwQtMJAiLRCQciQwDg0QkFASLlCT4AAAA"
		. "i0QkFAFUJBg7RCQoD4Ui@@@@i1wkIItEJDiJNCSDRCQ0AYu0JPwAAACLTCQ0AXQk"
		. "LDmMJOAAAAAPhbj+@@+LTCQMuq2L22iJ3w+vjCTkAAAAx0QkFAAAAADHRCQ0AAAA"
		. "AInIwfkf9+rB+gwpyolUJAiLdCQMi0wkCDHAOc6LTCQUD07wiXQkDIt0JDQ5zg9P"
		. "xolEJDSLhCTEAAAAK4Qk+AAAAIlEJCiLhCTIAAAAK4Qk@AAAAIO8JKQAAAADiUQk"
		. "LA+O4gEAAIuMJMQAAACLhCTMAAAAD6+MJMgAAACJygHChcl+CsYAAYPAATnCdfaD"
		. "vCSwAAAACQ+EcPz@@4uEJLAAAACD6AGD+AcPh64BAACD+AOJRCRAD46pAQAAi0Qk"
		. "KMdEJFAAAAAAxwQkAAAAAIlEJDiLRCQsiUQkPIt0JFA5dCQ4x0QkWAAAAAAPjO@8"
		. "@@+LdCRYOXQkPA+M9wUAAItMJFCLdCRAi0QkOCnI98YCAAAAD0TBi0wkWInCi0Qk"
		. "PCnI98YBAAAAD0TBg@4DidEPT8gPT8KJTCQciUQkGOnBBAAAjUcBxwQkAAAAAMdE"
		. "JAgAAAAAweAHiceLhCTEAAAAweACiUQkGIuEJMgAAACFwA+OsAAAAIuEJMQAAACF"
		. "wH5ci4wktAAAAItcJCiLrCS0AAAAA1wkCAHxA3QkGIl0JBQB9Q+2UQIPtkEBD7Yx"
		. "a8BLa9ImAcKJ8MHgBCnwAdA5xw+XA4PBBIPDATnpddWLjCTEAAAAAUwkCIt0JBSD"
		. "BCQBA3QkOIsEJDmEJMgAAAB1hekb+v@@kI20JgAAAACLtCTEAAAAAXQkGINEJBQB"
		. "i0QkFDmEJMgAAAAPjx@6@@+LvCSoAAAAi0QkSIlEJAiLRCRMiUQkFOnU@f@@jXYA"
		. "iVwkIOlL@f@@g7wk7AAAAAGDnCS8AAAA@+kx@v@@x0QkQAAAAACLRCQsx0QkUAAA"
		. "AADHBCQAAAAAiUQkOItEJCiJRCQ86VL+@@+J+MHoEA+vhCT8AAAAmfe8JOAAAAAP"
		. "r4QkuAAAAInBD7fHD6+EJPgAAACZ9@2NPIGLRCRIiUQkCItEJEyJRCQU6UL9@@+L"
		. "hCSsAAAAhcAPhCMEAACLjCSsAAAAi7Qk0AAAAIuEJNQAAACLnCTYAAAAiawk3AAA"
		. "AI08jjHJicWJfCQIizuDxgSDw1iDxQSJ+MHoEA+vhCT8AAAAmfe8JOAAAAAPr4Qk"
		. "uAAAAIkEJA+3xw+vhCT4AAAAmfe8JNwAAACLFCSNBIKJRvyLQ6yNBEGDwRaJRfw5"
		. "dCQIdaeLhCSsAAAAi4wk5AAAALqti9toD6@IiUQkDInIwfkf9+qJ0MH4DCnIi7Qk"
		. "2AAAAIlEJAjHRCQUAAAAAMdEJDQAAAAAg8YIiXQkJOld@P@@i4Qk4AAAAIucJMgA"
		. "AADRpCSsAAAAx0QkLAAAAADHRCQ8AAAAAA+vxQOEJNgAAACJRCQki4QkxAAAAMHg"
		. "AoXbiUQkVA+OK@7@@4m8JKgAAACLfCRci4wkxAAAAIXJD474AAAAi0QkKANEJDyL"
		. "nCS0AAAAiceLRCRUAfOJ3QHwif6JRCRAA4QktAAAAIlEJCDrE8YGAIPFBIPGATts"
		. "JCAPhKgAAAAPtkUCMf+JBCQPtkUBiUQkFA+2RQCJRCQYObwkrAAAAHbLi0QkJIsU"
		. "uIPHAotMuPwPtt4rXCQUidDB6BAPtsArBCSJXCQID7baK1wkGIH6@@@@AIlcJBwP"
		. "hosAAACLFCSNHFCNkwAEAAAPr9APr8KLVCQID6@SweILAdC6@gUAACnaidOLVCQc"
		. "D6@aD6@ajRQYOdFyhMYGAYPFBIPGATtsJCAPhVj@@@+LjCTEAAAAAUwkPIt0JECD"
		. "RCQsAQN0JDiLRCQsOYQkyAAAAA+F3@7@@4l8JFyLvCSoAAAA6dr2@@+NtCYAAAAA"
		. "icrB6hAPttoPttUPtsmJVCQEidqJXCQwD6@AiUwkEA+v0znQD48H@@@@i1wkCItM"
		. "JASJ2InKD6@DD6@ROdAPj+3+@@+LVCQci0wkEInTicgPr9oPr8E5ww+P0@7@@+lK"
		. "@@@@x0QkTAAAAADHRCRIAAAAAMdEJDQAAAAAx0QkDAAAAADp7vP@@4tEJBiFwA+I"
		. "5gAAAIt0JDw58A+P2gAAAItEJByFwA+IzgAAAIt0JDg58A+PwgAAAINEJGwBx0Qk"
		. "QAkAAACLRCQci7QkzAAAAA+vhCTEAAAAA0QkGIA8BgAPhIMAAACLTCQ0i3QkDDnx"
		. "D03xg7wkpAAAAAOJdCQgD48UAQAAhfYPhAQEAAADhCTMAAAAi1wkFDHSi3QkCInB"
		. "6yo5VCQ0fheLrCTUAAAAi0SVAAHI9gABdQWD6wF4KoPCATlUJCAPhMUDAAA7VCQM"
		. "fdCLrCTQAAAAi0SVAAHIgDgBd76D7gF5uYN8JEAJdAqDRCRYAek4+v@@i2wkRIXt"
		. "dC6DfCREAQ+EFQUAAIN8JEQCD4QABQAAMcCDfCREAw+UwClEJBiDRCRkAem59v@@"
		. "g2wkHAHr74NEJFAB6dv5@@8xwMdEJAwAAAAA6X38@@+LhCTEAAAAK4Qk+AAAAMdE"
		. "JAgAAAAAx0QkNAAAAADHRCQUAAAAAMdEJAwAAAAAiUQkKIuEJMgAAAArhCT8AAAA"
		. "iUQkLOkO+f@@i0QkHAOEJMAAAAAPr4QkuAAAAItUJBgDlCS8AAAAg7wkpAAAAAWN"
		. "BJCJRCRID4RTAQAAi7QktAAAAAH4i1QkIA+2dAYChdKJdCRMi7QktAAAAA+2dAYB"
		. "iXQkVIu0JLQAAAAPtgQGiUQkcA+EiAIAAItEJBSJvCSoAAAAMe2J94lEJHiLRCQI"
		. "iUQkdOt+jXYAjbwnAAAAADlsJDR+YYuEJNQAAACLVCRIi1wkTAMUqA+2TBcCD7ZE"
		. "FwErRCRUD7YUFytUJHCJzgHZKd6NmQAEAAAPr8APr97B4AsPr94Bw7j+BQAAKcgP"
		. "r8IPr8IBwzmcJKwAAAByB4NsJHgBeHyDxQE5bCQgD4TmAQAAO2wkDH2Gi4Qk0AAA"
		. "AItUJEiLXCRMAxSoD7ZMFwIPtkQXAStEJFQPthQXK1QkcInOAdkp3o2ZAAQAAA+v"
		. "wA+v3sHgCw+v3gHDuP4FAAApyA+vwg+vwgHDOZwkrAAAAA+DKP@@@4NsJHQBD4kd"
		. "@@@@i7wkqAAAAOnK@f@@i3QkXIX2D4VcAgAAi1wkIIXbD4RaAQAAi4Qk0AAAAItc"
		. "JCQx9om8JKgAAACJRCRMi4Qk1AAAAIlEJFRrRCQgFolEJHiLRCQIiUQkdIt8JEyL"
		. "RCRIifWJ2YlcJHADB4s7iXwkIIt8JFSLP4m8JKwAAACLvCS0AAAAD7Z8BwKJvCSE"
		. "AAAAi7wktAAAAA+2fAcBibwkiAAAAIu8JLQAAAAPtgQHiYQkjAAAAOl9AAAAjXYA"
		. "iwGLeQSDxQLB7xCJwsHqEIn7D7b7D7bSi1kEK5QkhAAAAIl8JDAPr@8Ptt+JXCQE"
		. "D7ZZBA+v0jn6iVwkEH84i1wkBA+21CuUJIgAAACJ3w+v0g+v+zn6fx6LXCQQD7bA"
		. "K4QkjAAAAInaD6@AD6@TOdAPjvIBAACDwQg7rCSsAAAAD4J5@@@@gXwkIP@@@wCL"
		. "XCRwdwuDbCR0AQ+Io@7@@4NEJEwEg8NYg0QkVASDxhY5dCR4D4Xh@v@@i7wkqAAA"
		. "AIuEJPAAAACDBCQBhcB0NotUJBwDlCTAAAAAi0QkGAOEJLwAAACLNCSLjCTwAAAA"
		. "weIQCdA7tCT0AAAAiUSx@A+NS@P@@4t0JBwxyYnwK4Qk@AAAAIPAAYnCifCLtCT8"
		. "AAAAD0jRjUQw@4t0JCw58A9Pxot0JBiJxYnwK4Qk+AAAAIPAAYnDifCLtCT4AAAA"
		. "D0jZjUQw@4t0JCg5xg9OxjnqicYPj6@7@@+LhCTEAAAAD6@CA4QkzAAAAInBjUUB"
		. "jW4BiUQkIDnzfw2J2MYEAQCDwAE56HX1g8IBA4wkxAAAADtUJCB13+ls+@@@vQMA"
		. "AADpofH@@4tMJCCFyQ+E@v7@@4tsJAgx9ou8JNAAAACLRCRIAwS3i7wk1AAAAIsc"
		. "t4u8JLQAAAAPtkwHAonaweoQD7bSKdEPtlQHAQ+2@w+vySn6i7wktAAAAA+2BAcP"
		. "tvsp+DlMJDB8Eg+v0jlUJAR8CQ+vwDlEJBB9BYPtAXgQg8YBOXQkIHWPid@pgP7@"
		. "@4nf6dH6@@+DRCQcAekE+@@@g0QkGAHp+vr@@4t8JAjGBAcC6d7v@@+BfCQg@@@@"
		. "AItcJHAPhxr+@@@pIP7@@78DAAAA6b3w@@+QkJCQkJA="
		x64:="QVdBVkFVQVRVV1ZTSIHsqAAAAEUx5ESLrCQwAQAAi5wkYAEAAImMJPAAAACLhCTw"
		. "AAAAidFEiYQkAAEAAESJjCQIAQAAi7wkaAEAAIPoAYP4BA+HxwUAAIO8JPAAAAAF"
		. "D4TQBQAAhf8PjgoNAABEiXwkMESLnCTwAAAARTHkTIu8JFABAACLrCSYAQAAMfZF"
		. "MfbHRCQcAAAAAESJVCQgRIlkJBCJlCT4AAAADx9EAABMY1QkHEUxyUUxwEwDlCRY"
		. "AQAAhdt@Net6Dx+AAAAAAEEPr8WJwUSJyJn3+wHBQ4A8AjF0PEmDwAFJY8ZBAelB"
		. "g8YBRDnDQYkMh35DifCZ9@9Bg@sEdckPr4QkGAEAAInBRInImff7Q4A8AjGNDIF1"
		. "xEiLlCRIAQAASYPAAUljxEEB6UGDxAFEOcOJDIJ@vQFcJByDRCQQAQO0JKABAACL"
		. "RCQQOccPhVX@@@9FieBBua2L22hEi1QkIEQPr4QkcAEAAESLfCQwi4wk+AAAAESJ"
		. "ZCQsRInAQcH4H0H36YnQwfgMRCnARIuEJHgBAACJRCRMRQ+vxkSJwEHB+B9B9+nB"
		. "+gxEKcKJVCRQg7wk8AAAAAQPhJcIAABJY+2LtCQgAQAASInoSAOEJEABAABIiUQk"
		. "OIuEJBgBAAAPr4QkKAEAAI0EsIu0JBgBAACJRCQgRIno99iDvCTwAAAAAY0EholE"
		. "JEAPhGEJAACDvCTwAAAAAg+EWgcAAESLjCQ4AQAARYXJD473AgAAQ41EbQBEiXQk"
		. "HESLdCQgRo0krQAAAAAx9jH@SJhIA4QkQAEAAEiJRCQQRYXtfllIi5wkEAEAAElj"
		. "xkUxyUyNRAMCSGPfSANcJBBBD7YQSYPABERr2iZBD7ZQ+2vCS0GNFANFD7ZY+kSJ"
		. "2MHgBEQp2AHQwfgHQogEC0mDwQFFOc1@yEUB5kQB74PGAUQDdCRAObQkOAEAAHWR"
		. "SI1FAUSLdCQci5QkAAEAADHbx0QkHAAAAABIiUQkILgBAAAASCnoQY1t@0SJdCRA"
		. "SIlEJDCLhCQ4AQAARI1g@0WF7Q+OzgAAAEhjdCQcSIt8JCBIi0QkEEyNHDdIi3wk"
		. "MEyNRDABSQHDTI0MN0iLfCQ4SQHBMcBMjTQ3Dx9EAABIhcAPhOoQAAA5xQ+E4hAA"
		. "AIXbD4TaEAAAQTncD4TREAAAQQ+2UP9BD7Zw@r8BAAAAAco58nI+QQ+2MDnycjZB"
		. "D7Zx@znyci1BD7Zz@znyciRBD7Zx@jnychtBD7YxOfJyE0EPtnP+OfJyCkEPtjM5"
		. "8kAPksdBiDwGSIPAAUmDwAFJg8MBSYPBAUE5xQ+Pbv@@@0QBbCQcg8MBOZwkOAEA"
		. "AA+FGf@@@0SLdCRAiZQkAAEAAESJdCQQRIl8JCBBjW3@TIt0JDhEi7wkOAEAAEUx"
		. "5Iu0JIABAAAx@0SJVCQciYwk+AAAAGYuDx+EAAAAAABFhe0PjsIAAABMY88xyUG7"
		. "AwAAAEcPthQOT41EDgFMA4wkQAEAAOttDx+EAAAAAABBjVL@uAIAAACD+gF2FEGD"
		. "+wEPlMKD+wEPlMAJ0A+2wAHAQYHi@QAAALoBAAAAdA9FhdtBD5TChdsPlMJECdKD"
		. "wQEJ0EE5zUGIAXRMhclFD7YQD4SfDwAARQ+2WP9Jg8ABSYPBATnpD4SmDgAAQQ+2"
		. "GIX2dYtBjUL@g@gCGcCD4AJBgeL9AAAAD5TCg8EBCdBBOc1BiAF1tEQB70GDxAFF"
		. "OecPjyj@@@9Ei3QkEESLVCQcRIt8JCCLjCT4AAAAi0QkTIlEJBCLRCRQiUQkHOkV"
		. "AwAAi3wkQItcJDjHRCR4AAAAAMdEJHQBAAAAx0QkSAAAAADHRCRsAAAAAIn4RI1P"
		. "AY1TAcHoH4lcJEQB+NH4iUQkIInYwegfAdjR+IlEJDCJ+ESJzw+v+kSNQAlBOdGJ"
		. "vCSIAAAAiceNQwmJfCRMi7wkiAAAAEEPT8BFMeSJxg+v8Dl8JHiJtCSMAAAAfVCL"
		. "vCSMAAAAOXwkbMdEJHAAAAAAfTuLXCR0OVwkcA+MpgcAAIt8JEiDRCRsAYn4g+AB"
		. "AcOJ+Iu8JIgAAACDwAGJXCR0g+ADOXwkeIlEJEh8sESJ4EiBxKgAAABbXl9dQVxB"
		. "XUFeQV@DMcCF0g+VwIlEJGgPhFkEAACJ+EGJ0g+20g+vw0HB6hBFD7bSweACSJhI"
		. "A4QkWAEAAEUPr9JIiUQkCA+2xUGJx0QPr@iJ0A+vwoX@iUQkKA+OZAgAAIu0JAAB"
		. "AACNQ@+JvCRoAQAAi3wkKESJrCQwAQAASI0EhQYAAADHRCQcAAAAAMdEJCAAAAAA"
		. "jVb@SIt0JAjHRCQsAAAAAEiJRCQwjQSdAAAAAImcJGABAABIjXSWBDHSiUQkQEGJ"
		. "1YusJGABAACF7Q+O+QAAAEhjRCQsSIucJFgBAAAx7UiNXAMCSANEJDBIA4QkWAEA"
		. "AEiJRCQQZi4PH4QAAAAAAESLpCQAAQAARA+2A0QPtkv@RA+2W@5FheR0PkiLVCQI"
		. "iwqJyMHoEA+2wEQpwA+vwEE5wnwbD7bFRCnID6@AQTnHfA0PtsFEKdgPr8A5x31b"
		. "SIPCBEg58nXHi0QkHE1j9UHB4BBBweEIQYPFAUUJyJlFCdj3vCRoAQAAD6+EJBgB"
		. "AABBicSJ6Jn3vCRgAQAASIuUJEgBAABBjQSEQokEskiLhCRQAQAARokEsEiDwwQD"
		. "rCSYAQAASDtcJBAPhT@@@@+LXCRAAVwkLINEJCABi5QkoAEAAItEJCABVCQcOYQk"
		. "aAEAAA+F1@7@@0SJbCQsRItEJCy6rYvbaEQPr4QkcAEAAESLrCQwAQAAx0QkHAAA"
		. "AABFMfZEicBBwfgf9+rB+gxEKcKJVCQQi3wkLItcJBAxwDnfD074RDt0JByJfCQs"
		. "RA9O8ESJ6CuEJJgBAACJRCRAi4QkOAEAACuEJKABAACDvCTwAAAAA4lEJDgPjn8B"
		. "AACLlCQ4AQAAQQ+v1YXSfhxIi4QkQAEAAIPqAUiNVBABxgABSIPAAUg5wnX0g7wk"
		. "CAEAAAkPhGj8@@+LhCQIAQAAg+gBg@gHD4dIAQAAg@gDiUQkUA+OQwEAAItEJEDH"
		. "RCRUAAAAAEUx5IlEJESLRCQ4iUQkTIt8JFQ5fCREx0QkZAAAAAAPjPP8@@+LfCRk"
		. "OXwkTA+MlgUAAItcJFSLfCRQi0QkRCnYQPbHAg9Ew4tcJGSJwotEJEwp2ED2xwEP"
		. "RMOD@wOJ0w9P2A9PwolcJDCJRCQg6VsEAABmDx9EAABEi4QkOAEAAIPBATH2weEH"
		. "Mf9CjSytAAAAAESLZCQgRYXAD46G+@@@RYXtfl5Ii5wkEAEAAEljxEUxyUyNRAMC"
		. "SGPfSANcJDgPH4QAAAAAAEEPthBBD7ZA@0UPtlj+a8BLa9ImAcJEidjB4AREKdgB"
		. "0DnBQg+XBAtJg8EBSYPABEU5zX@LQQHsRAHvg8YBRANkJEA5tCQ4AQAAdYzp4vn@"
		. "@4O8JIABAAABg5wkIAEAAP@pl@7@@8dEJFAAAAAAi0QkOMdEJFQAAAAARTHkiUQk"
		. "RItEJECJRCRM6bj+@@+JyMHoEA+vhCSgAQAAmff@D6+EJBgBAABBicAPt8EPr4Qk"
		. "mAEAAJn3+0GNDICLRCRMiUQkEItEJFCJRCQc6bj9@@9Ei7QkAAEAAEUxyTH2TIuc"
		. "JFgBAABFhfYPhCIEAABIi6wkSAEAAEyLpCRQAQAARIu0JKABAABBiwtJg8NYicjB"
		. "6BBBD6@Gmff@D6+EJBgBAABBicAPt8EPr4QkmAEAAJn3+0GNBIBCiUSNAEGLQ6yN"
		. "BEaDxhZDiQSMSYPBAUQ5jCQAAQAAd6+LhCQAAQAARIuEJHABAAC6rYvbaEQPr8CJ"
		. "RCQsRInAQcH4H@fqidDB+AxEKcBIi7wkWAEAAIlEJBBFMfbHRCQcAAAAAEiDxwhI"
		. "iXwkCOnf@P@@ifiLtCQ4AQAA0aQkAAEAAA+vw0iYSAOEJFgBAACF9kiJRCQID46L"
		. "+f@@QY1F@0SLXCRoRIukJAABAADHRCQwAAAAAMdEJEQAAAAASI0EhQYAAABEiXQk"
		. "fESJfCQciYwk+AAAAESJrCQwAQAASIlEJFhCjQStAAAAAIlEJGCLnCQwAQAAhdsP"
		. "jjIBAABIY0QkIEiLvCQQAQAASGN0JERIA3QkOEyNTAcCSANEJFhIAfhIiUQkEOsW"
		. "xgYASYPBBEiDxgFMO0wkEA+E3gAAAEEPtilFD7Zx@0Ux20UPtmn+SItcJAhFOeNz"
		. "z4sTQYPDAotLBInQD7b+RA+2wsHoEEQp90Up6A+2wCnogfr@@@8AdjmNFGgPr@9E"
		. "jboABAAARA+v+MHnC0EPr8cBx7j+BQAAKdCJwkEPr9CJ0EEPr8AB+DnBc1JIg8MI"
		. "65pBicoPttUPtslBweoQQYnPiUwkKEUPttKJVCQcRInRD6@AQQ+vyjnIf9CLVCQc"
		. "D6@@idAPr8I5x3@ARInARIn6QQ+vwEEPr9c50H+uxgYBSYPBBEiDxgFMO0wkEA+F"
		. "Iv@@@4t8JGABfCQgi7wkMAEAAAF8JESDRCQwAYtcJECLRCQwAVwkIDmEJDgBAAAP"
		. "haH+@@9EiVwkaESLdCR8RIt8JByLjCT4AAAARIusJDABAADpi@b@@8dEJFAAAAAA"
		. "x0QkTAAAAABFMfbHRCQsAAAAAOkh9P@@i0QkIIXAD4jvAAAAi3wkTDn4D4@jAAAA"
		. "i0QkMIXAD4jXAAAAi3wkRDn4D4@LAAAAg0QkeAHHRCRQCQAAAItEJDCLfCQgQQ+v"
		. "xUSNDDhIi7wkQAEAAEljwYA8BwAPhIgAAACLRCQsRDnwQQ9MxoO8JPAAAAADicYP"
		. "jxoBAACFwA+EYgQAAESLXCQci1wkEEUxwEiJ+OsvZpBBOdZ+G0iLvCRQAQAARInK"
		. "QgMUh@YEEAF1BkGD6wF4MkmDwAFEOcYPjiIEAABEOUQkLESJwn7JSIusJEgBAABE"
		. "ic9CA3yFAIA8OAF3s4PrAXmug3wkUAl0CoNEJGQB6Zn6@@+LVCRIhdJ0LoN8JEgB"
		. "D4SHBQAAg3wkSAIPhHIFAAAxwIN8JEgDD5TAKUQkIINEJHAB6Rv3@@+DbCQwAevv"
		. "g0QkVAHpPPr@@2YPH0QAADHAx0QkLAAAAADpZfz@@0SJ6CuEJJgBAADHRCQsAAAA"
		. "AEUx9sdEJBAAAAAAx0QkHAAAAACJRCRAi4QkOAEAACuEJKABAACJRCQ46XP5@@+L"
		. "RCQwA4QkKAEAAA+vhCQYAQAAi1QkIAOUJCABAACDvCTwAAAABQ+EkgEAAI0ckEiL"
		. "vCQQAQAAhfaNBBmNUAJIY9IPtiwXjVABSJhIY9IPtjwXiXwkWEiLvCQQAQAAD7YE"
		. "B4lEJGAPhOQCAACLRCQciYwk+AAAADH@SIuMJBABAACJhCSAAAAAi0QkEIlEJHzp"
		. "kwAAAEQ5tCSQAAAAfX1Ii4QkUAEAAIsUuAHajUICSJhED7YEAY1CAUhj0g+2FBFI"
		. "mCtUJGAPtgQBRYnDQQHoK0QkWEWNiAAEAABBKetFD6@LD6@ARQ+vy0G7@gUAAMHg"
		. "C0Upw0WJ2EQPr8JEAchBD6@QAdA5hCQAAQAAcg6DrCSAAAAAAQ+IlgAAAEiDxwE5"
		. "@g+O4AMAADt8JCyJvCSQAAAAD41c@@@@SIuEJEgBAACLFLgB2o1CAkiYRA+2BAGN"
		. "QgFIY9IPthQRSJgrVCRgD7YEAUWJw0EB6CtEJFhFjYgABAAAQSnrRQ+vyw+vwEUP"
		. "r8vB4AtBAcG4@gUAAEQpwA+vwg+vwkQByDmEJAABAAAPg+7+@@+DbCR8AQ+J4@7@"
		. "@4uMJPgAAADplf3@@40EkIlEJFiLRCRohcAPhY0CAACF9g+EdwEAAEyLjCRIAQAA"
		. "jUb@SIt8JAhMi5wkUAEAADH2SY1EgQRIiYQkgAAAAItEJBCJRCR8i2wkWEEDKYnz"
		. "RIsHiYwk+AAAAI1FAY1VAkhj7USJRCRgRYsDSJhIY9JIiYQkkAAAAEiLhCQQAQAA"
		. "RImEJAABAABJifgPtgQQSIuUJJAAAACJhCSYAAAASIuEJBABAAAPtgQQiYQkkAAA"
		. "AEiLhCQQAQAAD7YEKImEJJwAAADpgAAAAA8fgAAAAABBiwBBi2gEg8MCicJBiepI"
		. "ienB6hBBweoQD7bND7bSK5QkmAAAAEUPttJBic9AD7bNRInVQQ+v6olMJCgPr9I5"
		. "6n8yD7bUK5QkkAAAAESJ@UEPr+8Pr9I56n8aD7bAK4QknAAAAInKD6@RD6@AOdAP"
		. "jhgCAABJg8AIO5wkAAEAAA+Cev@@@4F8JGD@@@8Ai4wk+AAAAHcLg2wkfAEPiCD8"
		. "@@9Jg8EESIPHWEmDwwSDxhZMO4wkgAAAAA+FuP7@@0GDxAFIg7wkiAEAAAB0OYtU"
		. "JDADlCQoAQAATWPEi0QkIAOEJCABAABIi7wkiAEAAMHiEAnQRDukJJABAABCiUSH"
		. "@A+NWPP@@4t8JDAx0on4K4QkoAEAAIPAAUGJwIn4i7wkoAEAAEQPSMKNRDj@i3wk"
		. "ODn4D0@Hi3wkIInGifgrhCSYAQAAg8ABQYnBifiLvCSYAQAARA9Iyo1EOP+LfCRA"
		. "OfgPT8dBOfCJww+PUvv@@0SJ6kljwY1uAUEPr9BJY@1IY9JIAdBIA4QkQAEAAEmJ"
		. "w4nYRCnISI1wAUE52X8TSo0UHkyJ2MYAAEiDwAFIOdB19EGDwAFJAftBOeh13On@"
		. "+v@@uwMAAADpVPH@@4X2D4Tq@v@@i3wkEEyLjCQQAQAARTHbSIucJEgBAACLRCRY"
		. "QgMEm0iLnCRQAQAAQosMm41QAkhj0g+23UUPtgQRicrB6hAPttJBKdCNUAFImEEP"
		. "tgQBSGPSQQ+2FBFFD6@AKdoPttkp2EU5wnwRD6@SQTnXfAkPr8A5RCQofQmD7wEP"
		. "iG36@@9Jg8MBRDnef4bpXP7@@4NEJDAB6ZL6@@+DRCQgAemI+v@@i4wk+AAAAOk8"
		. "@v@@QcYEBgLpfO@@@4F8JGD@@@8Ai4wk+AAAAA+H9f3@@+n7@f@@QbsDAAAA6Vvw"
		. "@@+QkJCQkJCQkJCQkJCQkA=="
		MyFunc:=this.MCode(StrReplace((A_PtrSize=8?x64:x32),"@","/"))
	}
	text:=j[1], w:=j[2], h:=j[3]
		, err1:=this.Floor(j[4] ? j[5] : ini.err1)
		, err0:=this.Floor(j[4] ? j[6] : ini.err0)
		, mode:=j[7], color:=j[8], n:=j[9]
	return (!ini.bits.Scan0) ? 0 : DllCall(MyFunc.Ptr
		, "int",mode, "uint",color, "uint",n, "int",dir
		, "Ptr",ini.bits.Scan0, "int",ini.bits.Stride
		, "int",sx, "int",sy, "int",sw, "int",sh
		, "Ptr",ini.ss, "Ptr",ini.s1, "Ptr",ini.s0
		, "Ptr",text, "int",w, "int",h
		, "int",Floor(Abs(err1)*10000), "int",Floor(Abs(err0)*10000)
		, "int",(err1<0||err0<0), "Ptr",allpos_ptr, "int",ini.allpos_max
		, "int",Floor(w*ini.zoomW), "int",Floor(h*ini.zoomH))
}

code()
{
	return "
	(

//***** 机器码的 C语言 源代码 *****

int __attribute__((__stdcall__)) PicFind(
  int mode, unsigned int c, unsigned int n, int dir
			, unsigned char * Bmp, int Stride
			, int sx, int sy, int sw, int sh
			, unsigned char * ss, unsigned int * s1, unsigned int * s0
			, unsigned char * text, int w, int h
			, int err1, int err0, int more_err
			, unsigned int * allpos, int allpos_max
			, int new_w, int new_h )
{
  int ok, o, i, j, k, v, e1, e0, len1, len0, max;
  int x, y, x1, y1, x2, y2, x3, y3;
  int r, g, b, rr, gg, bb, dR, dG, dB;
  int ii, jj, RunDir, DirCount, RunCount, AllCount1, AllCount2;
  unsigned int c1, c2;
  unsigned char * ts, * gs;
  unsigned int * cors;
  ok=0; o=0; len1=0; len0=0; ts=ss+sw; gs=ss+sw*3;
  if (mode<1 || mode>5) goto Return1;
  //----------------------
  if (mode==5)
  {
    if (k=(c!=0))  // FindPic
    {
      cors=(unsigned int *)(text+w*h*4);
      r=(c>>16)&0xFF; g=(c>>8)&0xFF; b=c&0xFF; dR=r*r; dG=g*g; dB=b*b;
      for (y=0; y<h; y++)
      {
        for (x=0; x<w; x++, o+=4)
        {
          rr=text[2+o]; gg=text[1+o]; bb=text[o];
          for (i=0; i<n; i++)
          {
            c=cors[i];
            r=((c>>16)&0xFF)-rr; g=((c>>8)&0xFF)-gg; b=(c&0xFF)-bb;
            if (r*r<=dR && g*g<=dG && b*b<=dB) goto NoMatch1;
          }
          s1[len1]=(y*new_h/h)*Stride+(x*new_w/w)*4;
          s0[len1++]=(rr<<16)|(gg<<8)|bb;
          NoMatch1:;
        }
      }
    }
    else  // FindMultiColor or FindColor
    {
      cors=(unsigned int *)text;
      for (; len1<n; len1++, o+=22)
      {
        c=cors[o]; y=c>>16; x=c&0xFFFF;
        s1[len1]=(y*new_h/h)*Stride+(x*new_w/w)*4;
        s0[len1]=o+cors[o+1]*2;
      }
      cors+=2;
    }
    goto StartLookUp;
  }
  //----------------------
  // 生成查表需要的表格
  for (y=0; y<h; y++)
  {
    for (x=0; x<w; x++)
    {
      if (mode==4)
        i=(y*new_h/h)*Stride+(x*new_w/w)*4;
      else
        i=(y*new_h/h)*sw+(x*new_w/w);
      if (text[o++]=='1')
        s1[len1++]=i;
      else
        s0[len0++]=i;
    }
  }
  //----------------------
  // 颜色位置模式
  // 仅用于多色验证码的识别
  if (mode==4)
  {
    y=c>>16; x=c&0xFFFF;
    c=(y*new_h/h)*Stride+(x*new_w/w)*4;
    goto StartLookUp;
  }
  //----------------------
  // 生成二值化图像
  o=sy*Stride+sx*4; j=Stride-sw*4; i=0;
  if (mode==1)  // 颜色二值化
  {
    cors=(unsigned int *)(text+w*h); n=n*2;
    for (y=0; y<sh; y++, o+=j)
    {
      for (x=0; x<sw; x++, o+=4, i++)
      {
        rr=Bmp[2+o]; gg=Bmp[1+o]; bb=Bmp[o];
        for (k=0; k<n;)
        {
          c1=cors[k++]; c2=cors[k++];
          r=((c1>>16)&0xFF)-rr; g=((c1>>8)&0xFF)-gg; b=(c1&0xFF)-bb;
          if (c1>0xFFFFFF)
          {
            v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b<=c2) goto MatchOK1;
          }
          else
          {
            dR=(c2>>16)&0xFF; dG=(c2>>8)&0xFF; dB=c2&0xFF;
            if (r*r<=dR*dR && g*g<=dG*dG && b*b<=dB*dB) goto MatchOK1;
          }
        }
        ts[i]=0;
        continue;
        MatchOK1:
        ts[i]=1;
      }
    }
  }
  else if (mode==2)  // 灰度阈值二值化
  {
    c=(c+1)<<7;
    for (y=0; y<sh; y++, o+=j)
      for (x=0; x<sw; x++, o+=4, i++)
        ts[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15<c) ? 1:0;
  }
  else if (mode==3)  // 灰度差值二值化
  {
    for (y=0; y<sh; y++, o+=j)
    {
      for (x=0; x<sw; x++, o+=4, i++)
        gs[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15)>>7;
    }
    for (i=0, y=0; y<sh; y++)
    {
      for (x=0; x<sw; x++, i++)
      {
        if (x==0 || x==sw-1 || y==0 || y==sh-1)
          ts[i]=2;
        else
        {
          n=gs[i]+c;
          ts[i]=(gs[i-1]>n || gs[i+1]>n
			|| gs[i-sw]>n   || gs[i+sw]>n
			|| gs[i-sw-1]>n || gs[i-sw+1]>n
			|| gs[i+sw-1]>n || gs[i+sw+1]>n) ? 1:0;
        }
      }
    }
  }
  for (i=0, y=0; y<sh; y++)
  {
    for (x=0; x<sw; x++, i++)
    {
      r=ts[i];
      g=(x==0) ? 3 : ts[i-1];
      b=(x==sw-1) ? 3 : ts[i+1];
      if (more_err)
        ss[i]=(r==2||r==1||g==1||b==1)<<1|(r==2||r==0||g==0||b==0);
      else
        ss[i]=(r==2||r==1)<<1|(r==2||r==0);
    }
  }
  //----------------------
  StartLookUp:
  err1=len1*err1/10000;
  err0=len0*err0/10000;
  if (err1>=len1) len1=0;
  if (err0>=len0) len0=0;
  max=(len1>len0) ? len1 : len0;
  w=new_w; h=new_h;
  x1=0; y1=0; x2=sw-w; y2=sh-h;
  if (mode>3)
  {
    for (i=0, j=sw*sh; i<j; i++)
      ss[i]=1;
  }
  else
  {
    if (more_err) sx++;
  }
  // 1 ==> ( Left to Right ) Top to Bottom
  // 2 ==> ( Right to Left ) Top to Bottom
  // 3 ==> ( Left to Right ) Bottom to Top
  // 4 ==> ( Right to Left ) Bottom to Top
  // 5 ==> ( Top to Bottom ) Left to Right
  // 6 ==> ( Bottom to Top ) Left to Right
  // 7 ==> ( Top to Bottom ) Right to Left
  // 8 ==> ( Bottom to Top ) Right to Left
  // 9 ==> Center to Four Sides
  if (dir==9)
  {
    x=(x1+x2)/2; y=(y1+y2)/2; i=x2-x1+1; j=y2-y1+1;
    AllCount1=i*j; i=(i>j) ? i+8 : j+8;
    AllCount2=i*i; RunCount=0; DirCount=1; RunDir=0;
    for (ii=0; RunCount<AllCount1 && ii<AllCount2; ii++)
    {
      for(jj=0; jj<DirCount; jj++)
      {
        if(x>=x1 && x<=x2 && y>=y1 && y<=y2)
        {
          RunCount++;
          goto FindPos;
          FindPos_GoBak:;
        }
        if (RunDir==0) y--;
        else if (RunDir==1) x++;
        else if (RunDir==2) y++;
        else if (RunDir==3) x--;
      }
      if (RunDir & 1) DirCount++;
      RunDir = (++RunDir) & 3;
    }
    goto Return1;
  }
  if (dir<1 || dir>8) dir=1;
  if (--dir>3) { r=y1; y1=x1; x1=r; r=y2; y2=x2; x2=r; }
  for (y3=y1; y3<=y2; y3++)
  {
    for (x3=x1; x3<=x2; x3++)
    {
      y=(dir & 2) ? y1+y2-y3 : y3;
      x=(dir & 1) ? x1+x2-x3 : x3;
      if (dir>3) { r=y; y=x; x=r; }
      //----------------------
      FindPos:
      e1=err1; e0=err0; o=y*sw+x;
      if (ss[o]==0) goto NoMatch;
      if (mode<4)
      {
        for (i=0; i<max; i++)
        {
          if (i<len1 && ss[o+s1[i]]<2 && (--e1)<0) goto NoMatch;
          if (i<len0 && (ss[o+s0[i]]&1)==0 && (--e0)<0) goto NoMatch;
        }
      }
      else if (mode==5)
      {
        o=(sy+y)*Stride+(sx+x)*4;
        if (k)
        {
          for (i=0; i<max; i++)
          {
            j=o+s1[i]; c=s0[i]; r=Bmp[2+j]-((c>>16)&0xFF);
            g=Bmp[1+j]-((c>>8)&0xFF); b=Bmp[j]-(c&0xFF);
            if ((r*r>dR || g*g>dG || b*b>dB) && (--e1)<0) goto NoMatch;
          }
        }
        else
        {
          for (i=0; i<max; i++)
          {
            j=o+s1[i]; rr=Bmp[2+j]; gg=Bmp[1+j]; bb=Bmp[j];
            for (j=i*22, v=cors[j]>0xFFFFFF, n=s0[i]; j<n;)
            {
              c1=cors[j++]; c2=cors[j++];
              r=((c1>>16)&0xFF)-rr; g=((c1>>8)&0xFF)-gg; b=(c1&0xFF)-bb;
              dR=(c2>>16)&0xFF; dG=(c2>>8)&0xFF; dB=c2&0xFF;
              if (r*r<=dR*dR && g*g<=dG*dG && b*b<=dB*dB)
              {
                if (v) goto NoMatch2;
                goto MatchOK;
              }
            }
            if (v) continue;
            NoMatch2:
            if ((--e1)<0) goto NoMatch;
            MatchOK:;
          }
        }
      }
      else  // mode==4
      {
        o=(sy+y)*Stride+(sx+x)*4;
        j=o+c; rr=Bmp[2+j]; gg=Bmp[1+j]; bb=Bmp[j];
        for (i=0; i<max; i++)
        {
          if (i<len1)
          {
            j=o+s1[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b>n && (--e1)<0) goto NoMatch;
          }
          if (i<len0)
          {
            j=o+s0[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; v=r+rr+rr;
            if ((1024+v)*r*r+2048*g*g+(1534-v)*b*b<=n && (--e0)<0) goto NoMatch;
          }
        }
      }
      ok++;
      if (allpos!=0)
      {
        allpos[ok-1]=(sy+y)<<16|(sx+x);
        if (ok>=allpos_max) goto Return1;
      }
      // 跳过可能会重叠的区域
      r=y-h+1; r=(r>=0) ? r:0; rr=y+h-1; rr=(rr<=sh-h) ? rr:sh-h;
      g=x-w+1; g=(g>=0) ? g:0; gg=x+w-1; gg=(gg<=sw-w) ? gg:sw-w;
      for (i=r; i<=rr; i++)
      {
        for (j=g; j<=gg; j++)
          ss[i*sw+j]=0;
      }
      NoMatch:
      if (dir==9) goto FindPos_GoBak;
    }
  }
  //----------------------
  Return1:
  return ok;
}

	)"
}

PicInfo(text)
{
	local
	if !InStr(text, "$")
		return
	static init, info, bmp
	if !VarSetCapacity(init) && (init:="1")
		info:=[], bmp:=[]
	key:=(r:=StrLen(v:=Trim(text,"|")))<10000 ? v
: DllCall("ntdll\RtlComputeCrc32", "uint",0
		, "Ptr",&v, "uint",r*(1+!!A_IsUnicode), "uint")
	if info.HasKey(key)
		return info[key]
	comment:="", seterr:=err1:=err0:=0
	; You Can Add Comment Text within The <>
	if RegExMatch(v, "O)<([^>\n]*)>", r)
		v:=StrReplace(v,r[0]), comment:=Trim(r[1])
	; You can Add two fault-tolerant in the [], separated by commas
	if RegExMatch(v, "O)\[([^\]\n]*)]", r)
	{
		v:=StrReplace(v,r[0]), r:=StrSplit(r[1] ",", ",")
			, seterr:=1, err1:=r[1], err0:=r[2]
	}
	color:=SubStr(v,1,InStr(v,"$")-1), v:=Trim(SubStr(v,InStr(v,"$")+1))
	mode:=InStr(color,"##") ? 5 : InStr(color,"#") ? 4
: InStr(color,"**") ? 3 : InStr(color,"*") ? 2 : 1
	color:=RegExReplace(color, "[*#\s]")
	(mode=1 || mode=5) && color:=StrReplace(color,"0x")
  if (mode=5)
  {
    if !(v~="/[\s\-\w]+/[\s\-\w,/]+$")
    {
		; <FindPic> : Text 参数需要手动输入
		; Text:='|<>##DRDGDB-RRGGBB1-RRGGBB2... $ d:\a.bmp'
		; 0xRRGGBB1(+/-0xDRDGDB)... 都是透明色,不参与匹配
      if !(hBM:=LoadPicture(v))
        return
      this.GetBitmapWH(hBM, w, h)
      if (w<1 || h<1)
        return
      hBM2:=this.CreateDIBSection(w, h, 32, Scan0)
      this.CopyHBM(hBM2, 0, 0, hBM, 0, 0, w, h)
      DllCall("DeleteObject", "Ptr",hBM)
      if (!Scan0)
        return
		; 所有用于搜索的图片都缓存了
      StrReplace(color, "-",, n)
      bmp.Push(buf:=this.Buffer(w*h*4 + n*4)), v:=buf.Ptr, p:=v+w*h*4-4
      DllCall("RtlMoveMemory", "Ptr",v, "Ptr",Scan0, "Ptr",w*h*4)
      DllCall("DeleteObject", "Ptr",hBM2)
      For k1,v1 in StrSplit(color, "-")
      if (k1>1)
        NumPut(this.ToRGB(v1), 0|p+=4, "uint")
      color:=this.Floor("0x" StrSplit(color "-", "-")[1])|0x1000000
    }
    else
    {
		; <FindMultiColor> 或 <FindColor> : 找色 是仅有一个点的 找多色
		; Text:='|<>##DRDGDB $ 0/0/RRGGBB1-DRDGDB1/RRGGBB2, xn/yn/-RRGGBB3/RRGGBB4, ...'
		; '##'之后的颜色 (0xDRDGDB) 是所有颜色的默认偏色(各个分量允许的变化值)
		; 初始点 (0,0) 匹配 0xRRGGBB1(+/-0xDRDGDB1) 或者 0xRRGGBB2(+/-0xDRDGDB),
		; 点 (xn,yn) 匹配 排除 0xRRGGBB3(+/-0xDRDGDB) 和排除 0xRRGGBB4(+/-0xDRDGDB)
		; 点坐标后面以 '-' 开头表示要排除后面的所有颜色,其他颜色都匹配
		; 每个点最多允许匹配10组颜色 (xn/yn/RRGGBB1/.../RRGGBB10)
      arr:=StrSplit(Trim(RegExReplace(v, "i)\s|0x"), ","), ",")
      if !(n:=arr.Length())
        return
      bmp.Push(buf:=this.Buffer(n*22*4)), v:=buf.Ptr
			, color:=StrSplit(color "-", "-")[1]
      For k1,v1 in arr
      {
        r:=StrSplit(v1 "/", "/")
			, x:=this.Floor(r[1]), y:=this.Floor(r[2])
			, (A_Index=1) ? (x1:=x2:=x, y1:=y2:=y)
: (x1:=Min(x1,x), x2:=Max(x2,x), y1:=Min(y1,y), y2:=Max(y2,y))
      }
      For k1,v1 in arr
      {
        r:=StrSplit(v1 "/", "/")
		, x:=this.Floor(r[1])-x1, y:=this.Floor(r[2])-y1
		, n1:=Min(Max(r.Length()-3, 0), 10)
		, NumPut(y<<16|x, 0|p:=v+(A_Index-1)*22*4, "uint")
		, NumPut(n1, 0|p+=4, "uint")
        Loop % n1
          k1:=(InStr(v1:=r[2+A_Index], "-")=1 ? 0x1000000:0)
		, c:=StrSplit(Trim(v1,"-") "-" color, "-")
		, NumPut(this.ToRGB(c[1])&0xFFFFFF|k1, 0|p+=4, "uint")
		, NumPut(this.Floor("0x" c[2]), 0|p+=4, "uint")
      }
      color:=0, w:=x2-x1+1, h:=y2-y1+1
    }
  }
  else
  {
    r:=StrSplit(v ".", "."), w:=this.Floor(r[1])
		, v:=this.base64tobit(r[2]), h:=StrLen(v)//w
    if (w<1 || h<1 || StrLen(v)!=w*h)
      return
    arr:=StrSplit(Trim(color, "/"), "/")
    if !(n:=arr.Length())
      return
    bmp.Push(buf:=this.Buffer(StrPut(v, "CP0") + n*2*4))
		, StrPut(v, buf.Ptr, "CP0"), v:=buf.Ptr, p:=v+w*h-4
		, color:=this.Floor(arr[1])
    if (mode=1)
    {
      For k1,v1 in arr
      {
        k1:=(InStr(v1, "@") ? 0x1000000:0)
		, r:=StrSplit(v1 "@1", "@"), x:=this.Floor(r[2])
		, x:=(x<=0||x>1?1:x), x:=Floor(4606*255*255*(1-x)*(1-x))
		, c:=StrSplit(Trim(r[1],"-") "-" Format("{:X}",x), "-")
		, NumPut(this.ToRGB(c[1])&0xFFFFFF|k1, 0|p+=4, "uint")
		, NumPut(this.Floor("0x" c[2]), 0|p+=4, "uint")
      }
    }
    else if (mode=4)
    {
      r:=StrSplit(arr[1] "@1", "@"), n:=this.Floor(r[2])
		, n:=(n<=0||n>1?1:n), n:=Floor(4606*255*255*(1-n)*(1-n))
		, c:=this.Floor(r[1]), color:=((c-1)//w)<<16|Mod(c-1,w)
    }
  }
  return info[key]:=[v, w, h, seterr, err1, err0, mode, color, n, comment]
}

ToRGB(color)  ; color 可以使用: RRGGBB, Red, Yellow, Black, White
{
  static init, tab
  if !VarSetCapacity(init) && (init:="1")
    tab:=Object("Black", "000000", "White", "FFFFFF"
		, "Red", "FF0000", "Green", "008000", "Blue", "0000FF"
		, "Yellow", "FFFF00", "Silver", "C0C0C0", "Gray", "808080"
		, "Teal", "008080", "Navy", "000080", "Aqua", "00FFFF"
		, "Olive", "808000", "Lime", "00FF00", "Fuchsia", "FF00FF"
		, "Purple", "800080", "Maroon", "800000")
  return this.Floor("0x" (tab.HasKey(color)?tab[color]:color))
}

Buffer(size, FillByte:="")
{
  local
  buf:={}, buf.SetCapacity("_key", size), p:=buf.GetAddress("_key")
		, (FillByte!="" && DllCall("RtlFillMemory","Ptr",p,"Ptr",size,"uchar",FillByte))
		, buf.Ptr:=p, buf.Size:=size
  return buf
}

GetBitsFromScreen(ByRef x:=0, ByRef y:=0, ByRef w:=0, ByRef h:=0
		, ScreenShot:=1, ByRef zx:=0, ByRef zy:=0, ByRef zw:=0, ByRef zh:=0)
{
  local
  static init, CAPTUREBLT
  if !VarSetCapacity(init) && (init:="1")  ; thanks Descolada
  {
    DllCall("Dwmapi\DwmIsCompositionEnabled", "Int*",i:=0)
    CAPTUREBLT:=i ? 0 : 0x40000000
  }
  (!IsObject(this.bits) && this.bits:={Scan0:0, hBM:0, oldzw:0, oldzh:0})
		, bits:=this.bits
  if (!ScreenShot && bits.Scan0)
  {
    zx:=bits.zx, zy:=bits.zy, zw:=bits.zw, zh:=bits.zh
		, w:=Min(x+w,zx+zw), x:=Max(x,zx), w-=x
		, h:=Min(y+h,zy+zh), y:=Max(y,zy), h-=y
    return bits
  }
  bch:=A_BatchLines, cri:=A_IsCritical
  Critical
  bits.BindWindow:=id:=this.BindWindow(0,0,1)
  if (id)
  {
    WinGet, id, ID, ahk_id %id%
    WinGetPos, zx, zy, zw, zh, ahk_id %id%
  }
  if (!id)
  {
    SysGet, zx, 76
    SysGet, zy, 77
    SysGet, zw, 78
    SysGet, zh, 79
  }
  this.UpdateBits(bits, zx, zy, zw, zh)
		, w:=Min(x+w,zx+zw), x:=Max(x,zx), w-=x
		, h:=Min(y+h,zy+zh), y:=Max(y,zy), h-=y
  if (!ScreenShot || w<1 || h<1 || !bits.hBM)
  {
    Critical % cri
    SetBatchLines % bch
    return bits
  }
  if IsFunc(k:="GetBitsFromScreen2")
		&& %k%(bits, x-zx, y-zy, w, h)
  {
	; Get the bind window use bits.BindWindow
	; Each small range of data obtained from DXGI must be
	; copied to the screenshot cache using FindText().CopyBits()
    zx:=bits.zx, zy:=bits.zy, zw:=bits.zw, zh:=bits.zh
    Critical % cri
    SetBatchLines % bch
    return bits
  }
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",bits.hBM, "Ptr")
  if (id)
  {
    if (mode:=this.BindWindow(0,0,0,1))<2
    {
      hDC:=DllCall("GetDCEx", "Ptr",id, "Ptr",0, "int",3, "Ptr")
      DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",hDC, "int",x-zx, "int",y-zy, "uint",0xCC0020|CAPTUREBLT)
      DllCall("ReleaseDC", "Ptr",id, "Ptr",hDC)
    }
    else
    {
      hBM2:=this.CreateDIBSection(zw, zh)
      mDC2:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
      oBM2:=DllCall("SelectObject", "Ptr",mDC2, "Ptr",hBM2, "Ptr")
      DllCall("PrintWindow", "Ptr",id, "Ptr",mDC2, "uint",(mode>3)*3)
      DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",mDC2, "int",x-zx, "int",y-zy, "uint",0xCC0020)
      DllCall("SelectObject", "Ptr",mDC2, "Ptr",oBM2)
      DllCall("DeleteDC", "Ptr",mDC2)
      DllCall("DeleteObject", "Ptr",hBM2)
    }
  }
  else
  {
    hDC:=DllCall("GetWindowDC","Ptr",id:=DllCall("GetDesktopWindow","Ptr"),"Ptr")
    DllCall("BitBlt","Ptr",mDC,"int",x-zx,"int",y-zy,"int",w,"int",h
		, "Ptr",hDC, "int",x, "int",y, "uint",0xCC0020|CAPTUREBLT)
    DllCall("ReleaseDC", "Ptr",id, "Ptr",hDC)
  }
  if this.CaptureCursor(0,0,0,0,0,1)
    this.CaptureCursor(mDC, zx, zy, zw, zh)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteDC", "Ptr",mDC)
  Critical % cri
  SetBatchLines % bch
  return bits
}

UpdateBits(bits, zx, zy, zw, zh)
{
  local
  if (zw>bits.oldzw || zh>bits.oldzh || !bits.hBM)
  {
    Try DllCall("DeleteObject", "Ptr",bits.hBM)
    bits.hBM:=this.CreateDIBSection(zw, zh, bpp:=32, ppvBits)
		, bits.Scan0:=(!bits.hBM ? 0:ppvBits)
		, bits.Stride:=((zw*bpp+31)//32)*4
		, bits.oldzw:=zw, bits.oldzh:=zh
  }
  bits.zx:=zx, bits.zy:=zy, bits.zw:=zw, bits.zh:=zh
}

CreateDIBSection(w, h, bpp:=32, ByRef ppvBits:=0)
{
  local
  VarSetCapacity(bi, 40, 0), NumPut(40, bi, 0, "int")
		, NumPut(w, bi, 4, "int"), NumPut(-h, bi, 8, "int")
		, NumPut(1, bi, 12, "short"), NumPut(bpp, bi, 14, "short")
  return DllCall("CreateDIBSection", "Ptr",0, "Ptr",&bi
		, "int",0, "Ptr*",ppvBits:=0, "Ptr",0, "int",0, "Ptr")
}

GetBitmapWH(hBM, ByRef w, ByRef h)
{
  local
  VarSetCapacity(bm, size:=(A_PtrSize=8 ? 32:24))
		, DllCall("GetObject", "Ptr",hBM, "int",size, "Ptr",&bm)
		, w:=NumGet(bm,4,"int"), h:=Abs(NumGet(bm,8,"int"))
}

CopyHBM(hBM1, x1, y1, hBM2, x2, y2, w, h, Clear:=0)
{
  local
  if (w<1 || h<1 || !hBM1 || !hBM2)
    return
  mDC1:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM1:=DllCall("SelectObject", "Ptr",mDC1, "Ptr",hBM1, "Ptr")
  mDC2:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM2:=DllCall("SelectObject", "Ptr",mDC2, "Ptr",hBM2, "Ptr")
  DllCall("BitBlt", "Ptr",mDC1, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC2, "int",x2, "int",y2, "uint",0xCC0020)
  if (Clear)
    DllCall("BitBlt", "Ptr",mDC1, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC1, "int",x1, "int",y1, "uint",MERGECOPY:=0xC000CA)
  DllCall("SelectObject", "Ptr",mDC1, "Ptr",oBM1)
  DllCall("DeleteDC", "Ptr",mDC1)
  DllCall("SelectObject", "Ptr",mDC2, "Ptr",oBM2)
  DllCall("DeleteDC", "Ptr",mDC2)
}

CopyBits(Scan01,Stride1,x1,y1,Scan02,Stride2,x2,y2,w,h,Reverse:=0)
{
  local
  if (w<1 || h<1 || !Scan01 || !Scan02)
    return
  static init, MFCopyImage
  if !VarSetCapacity(init) && (init:="1")
  {
    MFCopyImage:=DllCall("GetProcAddress", "Ptr"
		, DllCall("LoadLibrary", "Str","Mfplat.dll", "Ptr")
		, "AStr","MFCopyImage", "Ptr")
  }
  if (MFCopyImage && !Reverse)  ; thanks QQ:任性
  {
    return DllCall(MFCopyImage
		, "Ptr",Scan01+y1*Stride1+x1*4, "int",Stride1
		, "Ptr",Scan02+y2*Stride2+x2*4, "int",Stride2
		, "uint",w*4, "uint",h)
  }
  ListLines % (lls:=A_ListLines)?0:0
  SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
  p1:=Scan01+(y1-1)*Stride1+x1*4
		, p2:=Scan02+(y2-1)*Stride2+x2*4, w*=4
  if (Reverse)
    p2+=(h+1)*Stride2, Stride2:=-Stride2
  Loop % h
    DllCall("RtlMoveMemory","Ptr",p1+=Stride1,"Ptr",p2+=Stride2,"Ptr",w)
  SetBatchLines % bch
  ListLines % lls
}

DrawHBM(hBM, lines)
{
  local
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM, "Ptr")
  oldc:="", brush:=0, VarSetCapacity(rect, 16)
  For k,v in lines  ; [ [x, y, w, h, color] ]
  if IsObject(v)
  {
    if (oldc!=v[5])
    {
      oldc:=v[5], BGR:=(oldc&0xFF)<<16|oldc&0xFF00|(oldc>>16)&0xFF
      DllCall("DeleteObject", "Ptr",brush)
      brush:=DllCall("CreateSolidBrush", "uint",BGR, "Ptr")
    }
    DllCall("SetRect", "Ptr",&rect, "int",v[1], "int",v[2]
		, "int",v[1]+v[3], "int",v[2]+v[4])
    DllCall("FillRect", "Ptr",mDC, "Ptr",&rect, "Ptr",brush)
  }
  DllCall("DeleteObject", "Ptr",brush)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteObject", "Ptr",mDC)
}

	; 绑定窗口从而可以后台查找这个窗口的图像
	; 相当于始终在前台。解绑窗口使用 FindText().BindWindow(0)

BindWindow(bind_id:=0, bind_mode:=0, get_id:=0, get_mode:=0)
{
  local
  (!IsObject(this.bind) && this.bind:={id:0, mode:0, oldStyle:0})
		, bind:=this.bind
  if (get_id)
    return bind.id
  if (get_mode)
    return bind.mode
  if (bind_id)
  {
    bind.id:=bind_id:=this.Floor(bind_id)
		, bind.mode:=bind_mode, bind.oldStyle:=0
    if (bind_mode & 1)
    {
      WinGet, i, ExStyle, ahk_id %bind_id%
      bind.oldStyle:=i
      WinSet, Transparent, 255, ahk_id %bind_id%
      Loop 30
      {
        Sleep 100
        WinGet, i, Transparent, ahk_id %bind_id%
      }
      Until (i=255)
    }
  }
  else
  {
    bind_id:=bind.id
    if (bind.mode & 1)
      WinSet, ExStyle, % bind.oldStyle, ahk_id %bind_id%
    bind.id:=0, bind.mode:=0, bind.oldStyle:=0
  }
}

	; 使用 FindText().CaptureCursor(1) 设置抓图时捕获鼠标
	; 使用 FindText().CaptureCursor(0) 取消抓图时捕获鼠标

CaptureCursor(hDC:=0, zx:=0, zy:=0, zw:=0, zh:=0, get_cursor:=0)
{
  local
  if (get_cursor)
    return this.Cursor
  if (hDC=1 || hDC=0) && (zw=0)
  {
    this.Cursor:=hDC
    return
  }
  VarSetCapacity(mi, 40, 0), NumPut(16+A_PtrSize, mi, "int")
  DllCall("GetCursorInfo", "Ptr",&mi)
  bShow:=NumGet(mi, 4, "int")
  hCursor:=NumGet(mi, 8, "Ptr")
  x:=NumGet(mi, 8+A_PtrSize, "int")
  y:=NumGet(mi, 12+A_PtrSize, "int")
  if (!bShow) || (x<zx || y<zy || x>=zx+zw || y>=zy+zh)
    return
  VarSetCapacity(ni, 40, 0)
  DllCall("GetIconInfo", "Ptr",hCursor, "Ptr",&ni)
  xCenter:=NumGet(ni, 4, "int")
  yCenter:=NumGet(ni, 8, "int")
  hBMMask:=NumGet(ni, (A_PtrSize=8?16:12), "Ptr")
  hBMColor:=NumGet(ni, (A_PtrSize=8?24:16), "Ptr")
  DllCall("DrawIconEx", "Ptr",hDC
		, "int",x-xCenter-zx, "int",y-yCenter-zy, "Ptr",hCursor
		, "int",0, "int",0, "int",0, "int",0, "int",3)
  DllCall("DeleteObject", "Ptr",hBMMask)
  DllCall("DeleteObject", "Ptr",hBMColor)
}

MCode(hex)
{
  local
  flag:=((hex~="[^\s\da-fA-F]")?1:4), hex:=RegExReplace(hex, "[\s=]")
  code:=this.Buffer(len:=(flag=1 ? StrLen(hex)//4*3+3 : StrLen(hex)//2))
  DllCall("crypt32\CryptStringToBinary", "Str",hex, "uint",0
		, "uint",flag, "Ptr",code.Ptr, "uint*",len, "Ptr",0, "Ptr",0)
  DllCall("VirtualProtect", "Ptr",code.Ptr, "Ptr",len, "uint",0x40, "Ptr*",0)
  return code
}

bin2hex(addr, size, base64:=1)
{
  local
  flag:=(base64 ? 1:4)|0x40000000, len:=0
  Loop 2
    DllCall("Crypt32\CryptBinaryToString", "Ptr",addr, "uint",size
		, "uint",flag, "Ptr",(A_Index=1?0:(p:=this.Buffer(len*2)).Ptr), "uint*",len)
  return RegExReplace(StrGet(p.Ptr, len), "\s+")
}

base64tobit(s)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  SetFormat, IntegerFast, d
  Loop Parse, Chars
    if InStr(s, A_LoopField, 1)
      s:=RegExReplace(s, "[" A_LoopField "]", ((i:=A_Index-1)>>5&1)
      . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1))
  s:=RegExReplace(RegExReplace(s,"[^01]+"),"10*$")
  ListLines % lls
  return s
}

bit2base64(s)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  s:=RegExReplace(s,"[^01]+")
  s.=SubStr("100000",1,6-Mod(StrLen(s),6))
  s:=RegExReplace(s,".{6}","|$0")
  Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  SetFormat, IntegerFast, d
  Loop Parse, Chars
    s:=StrReplace(s, "|" . ((i:=A_Index-1)>>5&1)
    . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1), A_LoopField)
  ListLines % lls
  return s
}

ASCII(s)
{
  local
  if RegExMatch(s, "O)\$(\d+)\.([\w+/]+)", r)
  {
    s:=RegExReplace(this.base64tobit(r[2]),".{" r[1] "}","$0`n")
    s:=StrReplace(StrReplace(s,"0","_"),"1","0")
  }
  else s:=""
  return s
}

	; 可以在脚本的开头用 FindText().PicLib(Text,1) 导入字库,
	; 然后使用 FindText().PicLib("说明文字1|说明文字2|...") 获取字库中的数据

PicLib(comments, add_to_Lib:=0, index:=1)
{
  local
  (!IsObject(this.Lib) && this.Lib:=[]), Lib:=this.Lib
		, (!Lib.HasKey(index) && Lib[index]:=[]), Lib:=Lib[index]
  if (add_to_Lib)
  {
    re:="O)<([^>\n]*)>[^$\n]+\$[^""\r\n]+"
    Loop Parse, comments, |
      if RegExMatch(A_LoopField, re, r)
      {
        s1:=Trim(r[1]), s2:=""
        Loop Parse, s1
          s2.=Format("_{:d}", Ord(A_LoopField))
        Lib[s2]:=r[0]
      }
    Lib[""]:=""
  }
  else
  {
    Text:=""
    Loop Parse, comments, |
    {
      s1:=Trim(A_LoopField), s2:=""
      Loop Parse, s1
        s2.=Format("_{:d}", Ord(A_LoopField))
      if Lib.HasKey(s2)
        Text.="|" . Lib[s2]
    }
    return Text
  }
}

	; 分割字符串为单个文字并获取数据

PicN(Number, index:=1)
{
  return this.PicLib(RegExReplace(Number,".","|$0"), 0, index)
}

	; 使用 FindText().PicX(Text) 可以将文字分割成多个单字的组合,从而适应间隔变化
	; 但是不能用于“颜色位置二值化”模式, 因为位置是与整体图像相关的

PicX(Text)
{
  local
  if !RegExMatch(Text, "O)(<[^$\n]+)\$(\d+)\.([\w+/]+)", r)
    return Text
  v:=this.base64tobit(r[3]), Text:=""
  c:=StrLen(StrReplace(v,"0"))<=StrLen(v)//2 ? "1":"0"
  txt:=RegExReplace(v,".{" r[2] "}","$0`n")
  While InStr(txt,c)
  {
    While !(txt~="m`n)^" c)
      txt:=RegExReplace(txt,"m`n)^.")
    i:=0
    While (txt~="m`n)^.{" i "}" c)
      i:=Format("{:d}",i+1)
    v:=RegExReplace(txt,"m`n)^(.{" i "}).*","$1")
    txt:=RegExReplace(txt,"m`n)^.{" i "}")
    if (v!="")
      Text.="|" r[1] "$" i "." this.bit2base64(v)
  }
  return Text
}

	; 截屏,作为后续操作要用的“上一次的截屏”

ScreenShot(x1:=0, y1:=0, x2:=0, y2:=0)
{
  this.FindText(,, x1, y1, x2, y2)
}

	; 从“上一次的截屏”中快速获取指定坐标的RGB颜色
	; 如果坐标超出了屏幕范围,将返回白色

GetColor(x, y, fmt:=1)
{
  local
  bits:=this.GetBitsFromScreen(,,,,0,zx,zy,zw,zh), x-=zx, y-=zy
		, c:=(x>=0 && x<zw && y>=0 && y<zh && bits.Scan0)
  ? NumGet(bits.Scan0+y*bits.Stride+x*4,"uint") : 0xFFFFFF
  return (fmt ? Format("0x{:06X}",c&0xFFFFFF) : c)
}

	; 在“上一次的截屏”中设置点的RGB颜色

SetColor(x, y, color:=0x000000)
{
  local
  bits:=this.GetBitsFromScreen(,,,,0,zx,zy,zw,zh), x-=zx, y-=zy
  if (x>=0 && x<zw && y>=0 && y<zh && bits.Scan0)
    NumPut(color, bits.Scan0+y*bits.Stride+x*4, "uint")
}

	; 根据 FindText() 的结果识别一行文字或验证码
	; offsetX 为两个文字的最大间隔,超过会插入*号
	; offsetY 为两个文字的最大高度差
	; overlapW 用于设置覆盖的宽度
	; 最后返回数组:{text:识别结果, x:结果左上角X, y:结果左上角Y, w:宽, h:高}

Ocr(ok, offsetX:=20, offsetY:=20, overlapW:=0)
{
  local
  ocr_Text:=ocr_X:=ocr_Y:=min_X:=dx:=""
  For k,v in ok
    x:=v.1
		, min_X:=(A_Index=1 || x<min_X ? x : min_X)
		, max_X:=(A_Index=1 || x>max_X ? x : max_X)
  While (min_X!="" && min_X<=max_X)
  {
    LeftX:=""
    For k,v in ok
    {
      x:=v.1, y:=v.2
      if (x<min_X) || (ocr_Y!="" && Abs(y-ocr_Y)>offsetY)
        Continue
	; Get the leftmost X coordinates
      if (LeftX="" || x<LeftX)
        LeftX:=x, LeftY:=y, LeftW:=v.3, LeftH:=v.4, LeftOCR:=v.id
    }
    if (LeftX="")
      Break
    if (ocr_X="")
      ocr_X:=LeftX, min_Y:=LeftY, max_Y:=LeftY+LeftH
	; If the interval exceeds the set value, add "*" to the result
    ocr_Text.=(ocr_Text!="" && LeftX>dx ? "*":"") . LeftOCR
	; Update for next search
    min_X:=LeftX+LeftW-(overlapW>LeftW//2 ? LeftW//2:overlapW)
		, dx:=LeftX+LeftW+offsetX, ocr_Y:=LeftY
		, (LeftY<min_Y && min_Y:=LeftY)
		, (LeftY+LeftH>max_Y && max_Y:=LeftY+LeftH)
  }
  if (ocr_X="")
    ocr_X:=0, min_Y:=0, min_X:=0, max_Y:=0
  return {text:ocr_Text, x:ocr_X, y:min_Y
		, w: min_X-ocr_X, h: max_Y-min_Y}
}

	; 按照从左到右、从上到下的顺序排序FindText()的结果
	; 忽略轻微的Y坐标差距,返回排序后的数组对象

Sort(ok, dy:=10)
{
  local
  if !IsObject(ok)
    return ok
  s:="", n:=150000, ypos:=[]
  For k,v in ok
  {
    x:=v.x, y:=v.y, add:=1
    For k1,v1 in ypos
    if Abs(y-v1)<=dy
    {
      y:=v1, add:=0
      Break
    }
    if (add)
      ypos.Push(y)
    s.=(y*n+x) "." k "|"
  }
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 以指定点为中心,按从近到远排序FindText()的结果,返回排序后的数组

Sort2(ok, px, py)
{
  local
  if !IsObject(ok)
    return ok
  s:=""
  For k,v in ok
    s.=((v.x-px)**2+(v.y-py)**2) "." k "|"
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 按指定的查找方向,排序FindText()的结果,返回排序后的数组

Sort3(ok, dir:=1)
{
  local
  if !IsObject(ok)
    return ok
  s:="", n:=150000
  For k,v in ok
    x:=v.1, y:=v.2
		, s.=(dir=1 ? y*n+x
: dir=2 ? y*n-x
: dir=3 ? -y*n+x
: dir=4 ? -y*n-x
: dir=5 ? x*n+y
: dir=6 ? x*n-y
: dir=7 ? -x*n+y
: dir=8 ? -x*n-y : y*n+x) "." k "|"
  s:=Trim(s,"|")
  Sort, s, N D|
  ok2:=[]
  Loop Parse, s, |
    ok2.Push( ok[StrSplit(A_LoopField,".")[2]] )
  return ok2
}

	; 提示某个坐标的位置,或远程控制中当前鼠标的位置

MouseTip(x:="", y:="", w:=10, h:=10, d:=3)
{
  local
  if (x="")
  {
    VarSetCapacity(pt,16,0), DllCall("GetCursorPos","Ptr",&pt)
    x:=NumGet(pt,0,"uint"), y:=NumGet(pt,4,"uint")
  }
  Loop 4
  {
    this.RangeTip(x-w, y-h, 2*w+1, 2*h+1, (A_Index & 1 ? "Red":"Blue"), d)
    Sleep 500
  }
  this.RangeTip()
}

	; 显示范围的边框,类似于 ToolTip

RangeTip(x:="", y:="", w:="", h:="", color:="Red", d:=3)
{
  local
  ListLines % (lls:=A_ListLines)?0:0
  static init, Range
  if !VarSetCapacity(init) && (init:="1")
    Range:=[0,0,0,0]
  if (x="")
  {
    Loop 4
      if (Range[i:=A_Index])
        Range[i].Destroy(), Range[i]:=""
    ListLines % lls
    return
  }
  if (!Range[1])
  {
    Loop 4
      Range[A_Index]:=Gui("+AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
  }
  x:=this.Floor(x), y:=this.Floor(y), w:=this.Floor(w), h:=this.Floor(h)
		, d:=this.Floor(d)
  Loop 4
  {
    i:=A_Index
		, x1:=(i=2 ? x+w : x-d)
		, y1:=(i=3 ? y+h : y-d)
		, w1:=(i=1 || i=3 ? w+2*d : d)
		, h1:=(i=2 || i=4 ? h+2*d : d)
    Range[i].BackColor:=color
    Range[i].Show("NA x" x1 " y" y1 " w" w1 " h" h1)
  }
  ListLines % lls
}

State(key)
{
  return GetKeyState(key,"P") || GetKeyState(key)
}

	; 用鼠标左右键选取屏幕范围

GetRange(ww:=25, hh:=8, key:="RButton")
{
  local
  static init, KeyOff, hk
  if !VarSetCapacity(init) && (init:="1")
    KeyOff:=this.GetRange.Bind(this, "Off")
  if (ww=="Off")
    return hk:=Trim(A_ThisHotkey, "*")
	;---------------------
  GetRange_HotkeyIf:=_Gui:=Gui()
  _Gui.Opt("-Caption +ToolWindow +E0x80000")
  _Gui.Title:="GetRange_HotkeyIf"
  _Gui.Show("NA x0 y0 w0 h0")
	;---------------------
  if GetKeyState("Ctrl")
    Send {Ctrl Up}
  Hotkey, IfWinExist, GetRange_HotkeyIf
  keys:=key "|Up|Down|Left|Right"
  For k,v in StrSplit(keys, "|")
  {
    if GetKeyState(v)
      Send {%v% Up}
    Hotkey, *%v%, %KeyOff%, On UseErrorLevel
  }
  Hotkey, IfWinExist
	;---------------------
  Critical % (cri:=A_IsCritical)?"Off":"Off"
  CoordMode, Mouse
  tip:=this.Lang("s5")
  hk:="", oldx:=oldy:="", keydown:=0
  Loop
  {
    Sleep 50
    MouseGetPos, x2, y2
    if (hk=key) || this.State(key) || this.State("Ctrl")
    {
      keydown++
      if (keydown=1)
        MouseGetPos, x1, y1, Bind_ID
      timeout:=A_TickCount+3000
      While (A_TickCount<timeout) && (this.State(key) || this.State("Ctrl"))
        Sleep 50
      hk:=""
      if (keydown>=2)
        Break
    }
    else if (hk="Up") || this.State("Up")
      (hh>1 && hh--), hk:=""
    else if (hk="Down") || this.State("Down")
      hh++, hk:=""
    else if (hk="Left") || this.State("Left")
      (ww>1 && ww--), hk:=""
    else if (hk="Right") || this.State("Right")
      ww++, hk:=""
    x:=(keydown?x1:x2), y:=(keydown?y1:y2)
    this.RangeTip(x-ww, y-hh, 2*ww+1, 2*hh+1, (A_MSec<500?"Red":"Blue"))
    if (oldx=x2 && oldy=y2)
      Continue
    oldx:=x2, oldy:=y2
    ToolTip % "x: " x " y: " y "`n" tip
  }
  ToolTip
  this.RangeTip()
  Hotkey, IfWinExist, GetRange_HotkeyIf
  For k,v in StrSplit(keys, "|")
    Hotkey, *%v%, %KeyOff%, Off UseErrorLevel
  Hotkey, IfWinExist
  GetRange_HotkeyIf.Destroy()
  Critical % cri
  return [x-ww, y-hh, x+ww, y+hh, Bind_ID]
}

GetRange2(key:="LButton")
{
  local
  FindText_GetRange:=_Gui:=Gui()
  _Gui.Opt("+LastFound +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
  _Gui.BackColor:="White"
  WinSet, Transparent, 10
  this.BitmapFromScreen(,,,,0,x,y,w,h)
  _Gui.Title:="FindText_GetRange"
  _Gui.Show("NA x" x " y" y " w" w " h" h)
  CoordMode, Mouse
  tip:=this.Lang("s7"), oldx:=oldy:=""
  Loop
  {
    Sleep 50
    MouseGetPos, x1, y1
    if (oldx=x1 && oldy=y1)
      Continue
    oldx:=x1, oldy:=y1
    ToolTip % "x: " x1 " y: " y1 " w: 0 h: 0`n" tip
  }
  Until this.State(key) || this.State("Ctrl")
  Loop
  {
    Sleep 50
    MouseGetPos, x2, y2
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
    this.RangeTip(x, y, w, h, (A_MSec<500 ? "Red":"Blue"))
    if (oldx=x2 && oldy=y2)
      Continue
    oldx:=x2, oldy:=y2
    ToolTip % "x: " x " y: " y " w: " w " h: " h "`n" tip
  }
  Until !(this.State(key) || this.State("Ctrl"))
  ToolTip
  this.RangeTip()
  FindText_GetRange.Destroy()
  Clipboard:=x ", " y ", " (x+w-1) ", " (y+h-1)
  return [x, y, x+w-1, y+h-1]
}

BitmapFromScreen(ByRef x:=0, ByRef y:=0, ByRef w:=0, ByRef h:=0
		, ScreenShot:=1, ByRef zx:=0, ByRef zy:=0, ByRef zw:=0, ByRef zh:=0)
{
  local
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy,zw,zh)
  if (w<1 || h<1 || !bits.hBM)
    return
  hBM:=this.CreateDIBSection(w, h)
  this.CopyHBM(hBM, 0, 0, bits.hBM, x-zx, y-zy, w, h, 1)
  return hBM
}

	; 快速保存截图为BMP文件,可用于调试
	; 如果 file = 0 或 "" ,会保存到剪贴板

SavePic(file:=0, x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  hBM:=this.BitmapFromScreen(x, y, w, h, ScreenShot)
  this.SaveBitmapToFile(file, hBM)
  DllCall("DeleteObject", "Ptr",hBM)
}

	; 保存图像到文件,如果 file = 0 或者 "",保存到剪贴板
	; 参数可以是位图句柄或者文件路径,例如: "c:\a.bmp"

SaveBitmapToFile(file, hBM_or_file, x:=0, y:=0, w:=0, h:=0)
{
  local
  if hBM_or_file is number
    hBM_or_file:="HBITMAP:*" hBM_or_file
  if !hBM:=DllCall("CopyImage", "Ptr",LoadPicture(hBM_or_file)
		, "int",0, "int",0, "int",0, "uint",0x2008)
    return
  if (file) || (w!=0 && h!=0)
  {
    (w=0 || h=0) && this.GetBitmapWH(hBM, w, h)
    hBM2:=this.CreateDIBSection(w, -h, bpp:=(file ? 24 : 32))
    this.CopyHBM(hBM2, 0, 0, hBM, x, y, w, h)
    DllCall("DeleteObject", "Ptr",hBM), hBM:=hBM2
  }
  VarSetCapacity(dib, dib_size:=(A_PtrSize=8 ? 104:84), 0)
		, DllCall("GetObject", "Ptr",hBM, "int",dib_size, "Ptr",&dib)
		, pbi:=&dib+(bitmap_size:=A_PtrSize=8 ? 32:24)
		, size:=NumGet(pbi+20, "uint"), pBits:=NumGet(pbi-A_PtrSize, "Ptr")
  if (!file)
  {
    hdib:=DllCall("GlobalAlloc", "uint",2, "Ptr",40+size, "Ptr")
    pdib:=DllCall("GlobalLock", "Ptr",hdib, "Ptr")
    DllCall("RtlMoveMemory", "Ptr",pdib, "Ptr",pbi, "Ptr",40)
    DllCall("RtlMoveMemory", "Ptr",pdib+40, "Ptr",pBits, "Ptr",size)
    DllCall("GlobalUnlock", "Ptr",hdib)
    DllCall("OpenClipboard", "Ptr",0)
    DllCall("EmptyClipboard")
    if !DllCall("SetClipboardData", "uint",8, "Ptr",hdib)
      DllCall("GlobalFree", "Ptr",hdib)
    DllCall("CloseClipboard")
  }
  else
  {
    if InStr(file,"\") && !FileExist(dir:=RegExReplace(file,"[^\\]*$"))
      Try FileCreateDir, % dir
    VarSetCapacity(bf, 14, 0), NumPut(0x4D42, bf, "short")
    NumPut(54+size, bf, 2, "uint"), NumPut(54, bf, 10, "uint")
    f:=FileOpen(file, "w"), f.RawWrite(bf, 14)
		, f.RawWrite(pbi+0, 40), f.RawWrite(pBits+0, size), f.Close()
  }
  DllCall("DeleteObject", "Ptr",hBM)
}

	; 显示保存的图像

ShowPic(file:="", show:=1, ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="")
{
  local
  if (file="")
  {
    this.ShowScreenShot()
    return
  }
  if !(hBM:=LoadPicture(file))
    return
  this.GetBitmapWH(hBM, w, h)
  bits:=this.GetBitsFromScreen(,,,,0,x,y,zw,zh)
  this.UpdateBits(bits, x, y, Max(w,zw), Max(h,zh))
  this.CopyHBM(bits.hBM, 0, 0, hBM, 0, 0, w, h)
  DllCall("DeleteObject", "Ptr",hBM)
  if (show)
    this.ShowScreenShot(x, y, x+w-1, y+h-1, 0)
}

	; 显示内存中的屏幕截图用于调试

ShowScreenShot(x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  static init, hPic, oldx, oldy, oldw, oldh, FindText_Screen
  if !VarSetCapacity(init) && (init:="1")
    FindText_Screen:=""
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
  {
    if (FindText_Screen)
      FindText_Screen.Destroy(), FindText_Screen:=""
    return
  }
  x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  if !hBM:=this.BitmapFromScreen(x,y,w,h,ScreenShot)
    return
	;---------------
  if (!FindText_Screen)
  {
    FindText_Screen:=_Gui:=Gui()  ; WS_EX_NOACTIVATE:=0x08000000
    _Gui.Opt("+AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")
    _Gui.MarginX:=0, _Gui.MarginY:=0
    id:=_Gui.Add("Pic", "w" w " h" h), hPic:=id.Hwnd
    _Gui.Title:="Show Pic"
    _Gui.Show("NA x" x " y" y " w" w " h" h)
    oldx:=x, oldy:=y, oldw:=w, oldh:=h
  }
  else if (oldx!=x || oldy!=y || oldw!=w || oldh!=h)
  {
    if (oldw!=w || oldh!=h)
      FindText_Screen[hPic].Move(,, w, h)
    FindText_Screen.Show("NA x" x " y" y " w" w " h" h)
    oldx:=x, oldy:=y, oldw:=w, oldh:=h
  }
  this.BitmapToWindow(hPic, 0, 0, hBM, 0, 0, w, h)
  DllCall("DeleteObject", "Ptr",hBM)
}

BitmapToWindow(hwnd, x1, y1, hBM, x2, y2, w, h)
{
  local
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM, "Ptr")
  hDC:=DllCall("GetDC", "Ptr",hwnd, "Ptr")
  DllCall("BitBlt", "Ptr",hDC, "int",x1, "int",y1, "int",w, "int",h
		, "Ptr",mDC, "int",x2, "int",y2, "uint",0xCC0020)
  DllCall("ReleaseDC", "Ptr",hwnd, "Ptr",hDC)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteDC", "Ptr",mDC)
}

	; 快速获取屏幕图像的搜索文本数据

GetTextFromScreen(x1:=0, y1:=0, x2:=0, y2:=0, Threshold:=""
		, ScreenShot:=1, ByRef rx:="", ByRef ry:="", cut:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    return this.Gui("CaptureS", ScreenShot)
  SetBatchLines % (bch:=A_BatchLines)?"-1":"-1"
  x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy)
  if (w<1 || h<1 || !bits.Scan0)
  {
    SetBatchLines % bch
    return
  }
  ListLines % (lls:=A_ListLines)?0:0
  gs:=[]
  j:=bits.Stride-w*4, p:=bits.Scan0+(y-zy)*bits.Stride+(x-zx)*4-j-4
  Loop % h + 0*(k:=0)
  Loop % w + 0*(p+=j)
    c:=NumGet(0|p+=4,"uint")
		, gs[++k]:=(((c>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
  if InStr(Threshold,"**")
  {
    Threshold:=StrReplace(Threshold,"*")
    if (Threshold="")
      Threshold:=50
    s:="", sw:=w, w-=2, h-=2, x++, y++
    Loop % h + 0*(y1:=0)
    Loop % w + 0*(y1++)
      i:=y1*sw+A_Index+1, j:=gs[i]+Threshold
		, s.=( gs[i-1]>j || gs[i+1]>j
		|| gs[i-sw]>j || gs[i+sw]>j
		|| gs[i-sw-1]>j || gs[i-sw+1]>j
		|| gs[i+sw-1]>j || gs[i+sw+1]>j ) ? "1":"0"
    Threshold:="**" Threshold
  }
  else
  {
    Threshold:=StrReplace(Threshold,"*")
    if (Threshold="")
    {
      pp:=[]
      Loop 256
        pp[A_Index-1]:=0
      Loop % w*h
        pp[gs[A_Index]]++
      IP0:=IS0:=0
      Loop 256
        k:=A_Index-1, IP0+=k*pp[k], IS0+=pp[k]
      Threshold:=Floor(IP0/IS0)
      Loop 20
      {
        LastThreshold:=Threshold
        IP1:=IS1:=0
        Loop % LastThreshold+1
          k:=A_Index-1, IP1+=k*pp[k], IS1+=pp[k]
        IP2:=IP0-IP1, IS2:=IS0-IS1
        if (IS1!=0 && IS2!=0)
          Threshold:=Floor((IP1/IS1+IP2/IS2)/2)
        if (Threshold=LastThreshold)
          Break
      }
    }
    s:=""
    Loop % w*h
      s.=gs[A_Index]<=Threshold ? "1":"0"
    Threshold:="*" Threshold
  }
  ListLines % lls
	;--------------------
  w:=Format("{:d}",w), CutUp:=CutDown:=0
  if (cut=1)
  {
    re1:="(^0{" w "}|^1{" w "})"
    re2:="(0{" w "}$|1{" w "}$)"
    While (s~=re1)
      s:=RegExReplace(s,re1), CutUp++
    While (s~=re2)
      s:=RegExReplace(s,re2), CutDown++
  }
  rx:=x+w//2, ry:=y+CutUp+(h-CutUp-CutDown)//2
  s:="|<>" Threshold "$" w "." this.bit2base64(s)
	;--------------------
  SetBatchLines % bch
  return s
}

	; 等待几秒钟直到屏幕图像改变,需要先调用FindText().ScreenShot()

WaitChange(time:=-1, x1:=0, y1:=0, x2:=0, y2:=0)
{
  local
  hash:=this.GetPicHash(x1, y1, x2, y2, 0)
  time:=this.Floor(time), timeout:=A_TickCount+Round(time*1000)
  Loop
  {
    if (hash!=this.GetPicHash(x1, y1, x2, y2, 1))
      return 1
    if (time>=0 && A_TickCount>=timeout)
      Break
    Sleep 10
  }
  return 0
}

	; 等待屏幕图像稳定下来

WaitNotChange(time:=1, timeout:=30, x1:=0, y1:=0, x2:=0, y2:=0)
{
  local
  oldhash:="", time:=this.Floor(time)
		, timeout:=A_TickCount+Round(this.Floor(timeout)*1000)
  Loop
  {
    hash:=this.GetPicHash(x1, y1, x2, y2, 1), t:=A_TickCount
    if (hash!=oldhash)
      oldhash:=hash, timeout2:=t+Round(time*1000)
    if (t>=timeout2)
      return 1
    if (t>=timeout)
      return 0
    Sleep 100
  }
}

GetPicHash(x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)
{
  local
  static init:=DllCall("LoadLibrary", "Str","ntdll", "Ptr")
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
  if (w<1 || h<1 || !bits.Scan0)
    return 0
  hash:=0, Stride:=bits.Stride, p:=bits.Scan0+(y-1)*Stride+x*4, w*=4
  ListLines % (lls:=A_ListLines)?0:0
  Loop % h
    hash:=(hash*31+DllCall("ntdll\RtlComputeCrc32", "uint",0
		, "Ptr",p+=Stride, "uint",w, "uint"))&0xFFFFFFFF
  ListLines % lls
  return hash
}

WindowToScreen(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  if (!id)
    WinGet, id, ID, A
  VarSetCapacity(rect, 16, 0)
		, DllCall("GetWindowRect", "Ptr",id, "Ptr",&rect)
		, x:=x1+NumGet(rect,"int"), y:=y1+NumGet(rect,4,"int")
}

ScreenToWindow(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  this.WindowToScreen(dx, dy, 0, 0, id), x:=x1-dx, y:=y1-dy
}

ClientToScreen(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  if (!id)
    WinGet, id, ID, A
  VarSetCapacity(pt, 8, 0), NumPut(0, pt, "int64")
		, DllCall("ClientToScreen", "Ptr",id, "Ptr",&pt)
		, x:=x1+NumGet(pt,"int"), y:=y1+NumGet(pt,4,"int")
}

ScreenToClient(ByRef x, ByRef y, x1, y1, id:="")
{
  local
  this.ClientToScreen(dx, dy, 0, 0, id), x:=x1-dx, y:=y1-dy
}

	; 不像 FindText 总是使用屏幕坐标,它使用与内置命令
	; ImageSearch 一样的 CoordMode 设置的坐标模式
	; 图片文件参数可以使用 "*n *TransRRGGBB-RRGGBB-White... d:\a.bmp"

ImageSearch(ByRef rx:="", ByRef ry:="", x1:=0, y1:=0, x2:=0, y2:=0
		, ImageFile:="", ScreenShot:=1, FindAll:=0, dir:=1)
{
  local
  dx:=dy:=0
  if (A_CoordModePixel="Window")
    this.WindowToScreen(dx, dy, 0, 0)
  else if (A_CoordModePixel="Client")
    this.ClientToScreen(dx, dy, 0, 0)
  text:=""
  Loop Parse, ImageFile, |
  if (v:=Trim(A_LoopField))!=""
  {
    text.=InStr(v,"$") ? "|" v : "|##"
    . (RegExMatch(v, "O)(^|\s)\*(\d+)\s", r)
    ? Format("{:06X}", r[2]<<16|r[2]<<8|r[2]) : "000000")
    . (RegExMatch(v, "Oi)(^|\s)\*Trans([\-\w]+)\s", r)
    ? "-" . Trim(r[2],"-") : "") . "$"
    . Trim(RegExReplace(v, "(^|\s)\*\S+"))
  }
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x1:=y1:=-n, x2:=y2:=n
  if (ok:=this.FindText(,, x1+dx, y1+dy, x2+dx, y2+dy
		, 0, 0, text, ScreenShot, FindAll,,,, dir))
  {
    For k,v in ok  ; you can use ok:=FindText().ok
      v.1-=dx, v.2-=dy, v.x-=dx, v.y-=dy
    rx:=ok[1].1, ry:=ok[1].2, ErrorLevel:=0
    return ok
  }
  else
  {
    rx:=ry:="", ErrorLevel:=1
    return 0
  }
}

	; 不像 FindText 总是使用屏幕坐标,它使用与内置命令
	; PixelSearch 一样的 CoordMode 设置的坐标模式
	; 颜色参数可以是 "RRGGBB-DRDGDB|RRGGBB-DRDGDB", Variation 取值 0-255

PixelSearch(ByRef rx:="", ByRef ry:="", x1:=0, y1:=0, x2:=0, y2:=0
		, ColorID:="", Variation:=0, ScreenShot:=1, FindAll:=0, dir:=1)
{
  local
  n:=this.Floor(Variation), text:=Format("##{:06X}$0/0/", n<<16|n<<8|n)
  . Trim(StrReplace(ColorID, "|", "/"), "/")
  return this.ImageSearch(rx, ry, x1, y1, x2, y2, text, ScreenShot, FindAll, dir)
}

	; 屏幕坐标指示的范围内的某些颜色的像素计数
	; 颜色参数可以是 "RRGGBB-DRDGDB|RRGGBB-DRDGDB", Variation 取值 0-255

PixelCount(x1:=0, y1:=0, x2:=0, y2:=0, ColorID:="", Variation:=0, ScreenShot:=1)
{
  local
  x1:=this.Floor(x1), y1:=this.Floor(y1), x2:=this.Floor(x2), y2:=this.Floor(y2)
  if (x1=0 && y1=0 && x2=0 && y2=0)
    n:=150000, x:=y:=-n, w:=h:=2*n
  else
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1
  bits:=this.GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy), x-=zx, y-=zy
  sum:=0, VarSetCapacity(s1,4), VarSetCapacity(s0,4)
  ini:={ bits:bits, ss:0, s1:&s1, s0:&s0
		, err1:0, err0:0, allpos_max:0, zoomW:1, zoomH:1 }
  n:=this.Floor(Variation), text:=Format("##{:06X}$0/0/", n<<16|n<<8|n)
  . Trim(StrReplace(ColorID, "|", "/"), "/")
  if (w>0 && h>0 && bits.Scan0) && IsObject(j:=this.PicInfo(text))
    sum:=this.PicFind(ini, j, 1, x, y, w, h, 0)
  return sum
}

	; 创建包含特定颜色的色块,可以限定这个色块中符合颜色的数量
	; ColorID 可以使用 "RRGGBB1@0.8|RRGGBB2-DRDGDB2"
	; Count 是这个色块中符合颜色的数量的最小值

ColorBlock(ColorID, w, h, Count)
{
  local
  Text:="|<>[" (1-Count/(w*h)) ",1]"
  . Trim(StrReplace(ColorID, "|", "/"), "/") . Format("${:d}.",w)
  . this.bit2base64(StrReplace(Format(Format("}:0{:d}d{",w*h),0),"0","1"))
  return Text
}

Click(x:="", y:="", other1:="", other2:="", GoBack:=0)
{
  local
  CoordMode, Mouse, % (bak:=A_CoordModeMouse)?"Screen":"Screen"
  if GoBack
    MouseGetPos, oldx, oldy
  MouseMove, x, y, 0
  Click % x "," y "," other1 "," other2
  if GoBack
    MouseMove, oldx, oldy, 0
  CoordMode, Mouse, %bak%
  return 1
}

	; 使用 ControlClick 代替 Click, 使用屏幕坐标,如果用于后台请提供 hwnd

ControlClick(x, y, WhichButton:="", ClickCount:=1, Opt:="", hwnd:="")
{
  local
  if !hwnd
    hwnd:=DllCall("WindowFromPoint", "int64",y<<32|x&0xFFFFFFFF, "Ptr")
  VarSetCapacity(pt,8,0), ScreenX:=x, ScreenY:=y
  Loop
  {
    NumPut(0,pt,"int64"), DllCall("ClientToScreen", "Ptr",hwnd, "Ptr",&pt)
		, x:=ScreenX-NumGet(pt,"int"), y:=ScreenY-NumGet(pt,4,"int")
		, id:=DllCall("ChildWindowFromPoint", "Ptr",hwnd, "int64",y<<32|x, "Ptr")
    if (!id || id=hwnd)
      Break
    else hwnd:=id
  }
  DetectHiddenWindows % (bak:=A_DetectHiddenWindows)?1:1
  PostMessage, 0x200, 0, y<<16|x,, ahk_id %hwnd%  ; WM_MOUSEMOVE
  SetControlDelay -1
  ControlClick, x%x% y%y%, ahk_id %hwnd%,, %WhichButton%, %ClickCount%, NA Pos %Opt%
  DetectHiddenWindows % bak
  return 1
}

	; 动态运行AHK代码作为新线程

Class Thread
{
  __New(args*)
  {
    this.pid:=this.Exec(args*)
  }
  __Delete()
  {
    Process, Close, % this.pid
  }
  Exec(s, Ahk:="", args:="")    ; required AHK v1.1.34+ and Ahk2Exe Use .exe
  {
    local
    Ahk:=Ahk ? Ahk : A_IsCompiled ? A_ScriptFullPath : A_AhkPath
    s:="`nDllCall(""SetWindowText"",""Ptr"",A_ScriptHwnd,""Str"",""<AHK>"")`n"
      . "`nSetBatchLines,-1`n" . s, s:=RegExReplace(s, "\R", "`r`n")
    Try
    {
      shell:=ComObjCreate("WScript.Shell")
      oExec:=shell.Exec("""" Ahk """ /script /force /CP0 * " args)
      oExec.StdIn.Write(s)
      oExec.StdIn.Close(), pid:=oExec.ProcessID
    }
    Catch
    {
      f:=A_Temp "\~ahk.tmp"
      s:="`r`nTry FileDelete " f "`r`n" s
      Try FileDelete %f%
      FileAppend %s%, %f%
      r:=this.Clear.Bind(this)
      SetTimer %r%, -3000
      Run "%Ahk%" /script /force /CP0 "%f%" %args%,, UseErrorLevel, pid
    }
    return pid
  }
  Clear()
  {
    Try FileDelete % A_Temp "\~ahk.tmp"
    SetTimer,, Off
  }
}

	; FindText().QPC() 用法类似于 A_TickCount

QPC()
{
  static init, f, c
  if !VarSetCapacity(init) && (init:="1")
    f:=0, c:=DllCall("QueryPerformanceFrequency", "Int64*",f)+(f/=1000)
  return (!DllCall("QueryPerformanceCounter","Int64*",c))*0+(c/f)
}

	; FindText().ToolTip() 用法类似于 ToolTip

ToolTip(s:="", x:="", y:="", num:=1, arg:="")
{
  local
  static init, ini, tip, timer
  if !VarSetCapacity(init) && (init:="1")
    ini:=[], tip:=[], timer:=[]
  f:="ToolTip_" . this.Floor(num)
  if (s="")
  {
    Try tip[f].Destroy()
    ini[f]:="", tip[f]:=""
    return
  }
	;-----------------
  r1:=A_CoordModeToolTip
  r2:=A_CoordModeMouse
  CoordMode Mouse, Screen
  MouseGetPos x1, y1
  CoordMode Mouse, %r1%
  MouseGetPos x2, y2
  CoordMode Mouse, %r2%
  (x!="" && x:="x" (this.Floor(x)+x1-x2))
		, (y!="" && y:="y" (this.Floor(y)+y1-y2))
		, (x="" && y="" && x:="x" (x1+16) " y" (y1+16))
	;-----------------
  bgcolor:=arg.bgcolor!="" ? arg.bgcolor : "FAFBFC"
  color:=arg.color!="" ? arg.color : "Black"
  font:=arg.font ? arg.font : "Consolas"
  size:=arg.size ? arg.size : "10"
  bold:=arg.bold ? arg.bold : ""
  trans:=arg.trans!="" ? arg.trans & 255 : 255
  timeout:=arg.timeout!="" ? arg.timeout : ""
	;-----------------
  r:=bgcolor "|" color "|" font "|" size "|" bold "|" trans "|" s
  if (!ini.HasKey(f) || ini[f]!=r)
  {
    ini[f]:=r
    Try tip[f].Destroy()
    tip[f]:=_Gui:=Gui()  ; WS_EX_LAYERED:=0x80000, WS_EX_TRANSPARENT:=0x20
    _Gui.Opt("+LastFound +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x80020")
    _Gui.MarginX:=2, _Gui.MarginY:=2
    _Gui.BackColor:=bgcolor
    _Gui.SetFont("c" color " s" size " " bold, font)
    _Gui.Add("Text",, s)
    _Gui.Title:=f
    _Gui.Show("Hide")
    WinSet, Transparent, % trans
  }
  tip[f].Opt("+AlwaysOnTop")
  tip[f].Show("NA " x " " y)
  if (timeout)
  {
    (!timer.HasKey(f) && timer[f]:=this.ToolTip.Bind(this,"","","",num))
		, r:=timer[f]
    SetTimer, %r%, % -Round(Abs(this.Floor(timeout)*1000))-1
  }
}

	; FindText().ObjView() 查看对象的值用于调试

ObjView(obj, keyname:="")
{
  local
  if IsObject(obj)  ; thanks lexikos's type(v)
  {
    s:=""
    For k,v in obj
      s.=this.ObjView(v, keyname "[" (StrLen(k)>1000
		|| [k].GetCapacity(1) ? """" k """":k) "]")
  }
  else
    s:=keyname ": " (StrLen(obj)>1000
		|| [obj].GetCapacity(1) ? """" obj """":obj) "`n"
  if (keyname!="")
    return s
	;------------------
  _Gui:=Gui("+AlwaysOnTop")
  _Gui.Add("Button", "y270 w350 gCancel Default", "OK")
  _Gui.Add("Edit", "xp y10 w350 h250 -Wrap -WantReturn")
  _Gui["Edit1"].Value:=s
  _Gui.Title:="Debug view object values"
  _Gui.Show()
  DetectHiddenWindows 0
  WinWaitClose % "ahk_id " _Gui.Hwnd
  _Gui.Destroy()
}

EditScroll(hEdit, regex:="", line:=0, pos:=0)
{
  local
  ControlGetText, s,, ahk_id %hEdit%
  pos:=(regex!="") ? InStr(SubStr(s,1,s~=regex),"`n",0,0)
: (line>1) ? InStr(s,"`n",0,1,line-1) : pos
  SendMessage, 0xB1, pos, pos,, ahk_id %hEdit%
  SendMessage, 0xB7,,,, ahk_id %hEdit%
}

	; 获取最后添加的Gui控件的对象,前提是 Gui +LastFound

LastCtrl()
{
  local
  return (G:=GuiFromHwnd(WinExist()))[G.LastHwnd]
}

	; 隐藏窗口,前提是 Gui +LastFound

Hide(args*)
{
  WinMinimize
  WinHide
  ToolTip
  DetectHiddenWindows 0
  WinWaitClose % "ahk_id " WinExist()
}


	;==== Optional GUI interface ====


Gui(cmd, arg1:="", args*)
{
  local
  static
  local bch, cri, lls, _Gui
  ListLines, % InStr("MouseMove|ToolTipOff",cmd)?0:A_ListLines
  static init
  if !VarSetCapacity(init) && (init:="1")
  {
    SavePicDir:=A_Temp "\Ahk_ScreenShot\"
    G_ := this.Gui.Bind(this)
    G_G := this.Gui.Bind(this, "G")
    G_Run := this.Gui.Bind(this, "Run")
    G_Show := this.Gui.Bind(this, "Show")
    G_KeyDown := this.Gui.Bind(this, "KeyDown")
    G_LButtonDown := this.Gui.Bind(this, "LButtonDown")
    G_RButtonDown := this.Gui.Bind(this, "RButtonDown")
    G_MouseMove := this.Gui.Bind(this, "MouseMove")
    G_ScreenShot := this.Gui.Bind(this, "ScreenShot")
    G_ShowPic := this.Gui.Bind(this, "ShowPic")
    G_Slider := this.Gui.Bind(this, "Slider")
    G_ToolTip := this.Gui.Bind(this, "ToolTip")
    G_ToolTipOff := this.Gui.Bind(this, "ToolTipOff")
    G_SaveScr := this.Gui.Bind(this, "SaveScr")
    FindText_Capture:=FindText_Main:=FindText_SubPic:=""
    bch:=A_BatchLines, cri:=A_IsCritical
    Critical
#NoEnv
    Lang:=this.Lang(,1), Tip_Text:=this.Lang(,2)
    G_.Call("MakeCaptureWindow")
    G_.Call("MakeMainWindow")
    OnMessage(0x100, G_KeyDown)
    OnMessage(0x201, G_LButtonDown)
    OnMessage(0x204, G_RButtonDown)
    OnMessage(0x200, G_MouseMove)
    Menu, Tray, Add
    Menu, Tray, Add, % Lang["s1"], % G_Show
    if (!A_IsCompiled && A_LineFile=A_ScriptFullPath)
    {
      Menu, Tray, Default, % Lang["s1"]
      Menu, Tray, Click, 1
      Menu, Tray, Icon, Shell32.dll, 23
    }
    Critical % cri
    SetBatchLines % bch
    Gui("+LastFound").Destroy()
	;-------------------
    Pics:=PrevControl:=x:=y:=oldx:=oldy:="", dx:=dy:=oldt:=0
  }
  Switch cmd
  {
  Case "G":
    id:=this.LastCtrl()
    Try id.OnEvent("Click", G_Run)
    Catch
      Try id.OnEvent("Change", G_Run)
    return
  Case "Run":
    Critical
    G_.Call(arg1.Name)
    return
  Case "Show":
    FindText_Main.Show(arg1 ? "Center" : "")
    ControlFocus,, % "ahk_id " hscr
    return
  Case "Cancel", "Cancel2":
    WinHide
    return
  Case "MakeCaptureWindow":
    WindowColor:="0xDDEEFF"
    Try FindText_Capture.Destroy()
    FindText_Capture:=_Gui:=Gui()
    _Gui.Opt("+LastFound +AlwaysOnTop -DPIScale")
    _Gui.MarginX:=15, _Gui.MarginY:=10
    _Gui.BackColor:=WindowColor
    _Gui.SetFont("s12", "Verdana")
    Tab:=_Gui.Add("Tab3", "vMyTab1 -Wrap", StrSplit(Lang["s18"],"|"))
    Tab.UseTab(1)
    C_:=[], nW:=71, nH:=25, w:=h:=12, pW:=nW*(w+1)-1, pH:=(nH+1)*(h+1)-1
    _Gui.Opt("-Theme")
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nW*(nH+1)
    {
      i:=A_Index, j:=i=1 ? "Section" : Mod(i,nW)=1 ? "xs y+1":"x+1"
      id:=_Gui.Add("Progress", j " w" w " h" h " -E0x20000 Smooth")
      C_[i]:=id.Hwnd
    }
    ListLines % lls
    _Gui.Opt("+Theme")
    _Gui.Add("Slider", "xs w" pW " vMySlider1 +Center Page20 Line10 NoTicks AltSubmit")
    G_G.Call()
    _Gui.Add("Slider", "ys h" pH " vMySlider2 +Center Page20 Line10 NoTicks AltSubmit +Vertical")
    G_G.Call()
    Tab.UseTab(2)
    id:=_Gui.Add("Text", "w" (pW-135) " h" pH " +Border Section"), parent_id:=id.Hwnd
    _Gui.Add("Slider", "xs wp vMySlider3 +Center Page20 Line10 NoTicks AltSubmit")
    G_G.Call()
    _Gui.Add("Slider", "ys h" pH " vMySlider4 +Center Page20 Line10 NoTicks AltSubmit +Vertical")
    G_G.Call()
    _Gui.Add("ListBox", "ys w120 h200 vSelectBox AltSubmit 0x100")
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vClearAll", Lang["ClearAll"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vOpenDir", Lang["OpenDir"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vLoadPic", Lang["LoadPic"])
    G_G.Call()
    _Gui.Add("Button", "y+0 wp vSavePic", Lang["SavePic"])
    G_G.Call()
    Tab.UseTab()
	;--------------
    _Gui.Add("Text", "xm Section", Lang["SelGray"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelGray ReadOnly")
    _Gui.Add("Text", "x+15 ys", Lang["SelColor"])
    _Gui.Add("Edit", "x+5 yp-3 w150 vSelColor ReadOnly")
    _Gui.Add("Text", "x+15 ys", Lang["SelR"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelR ReadOnly")
    _Gui.Add("Text", "x+5 ys", Lang["SelG"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelG ReadOnly")
    _Gui.Add("Text", "x+5 ys", Lang["SelB"])
    _Gui.Add("Edit", "x+5 yp-3 w80 vSelB ReadOnly")
	;--------------
    id:=_Gui.Add("Button", "xm Hidden Section", Lang["Auto"])
    id.GetPos(pX, pY, pW, pH)
    w:=Round(pW*0.75), i:=Round(w*3+15+pW*0.5-w*1.5)
    _Gui.Add("Button", "xm+" i " yp w" w " hp -Wrap vRepU", Lang["RepU"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutU", Lang["CutU"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutU3", Lang["CutU3"])
    G_G.Call()
    _Gui.Add("Button", "xm wp hp -Wrap vRepL", Lang["RepL"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutL", Lang["CutL"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutL3", Lang["CutL3"])
    G_G.Call()
    _Gui.Add("Button", "x+15 w" pW " hp -Wrap vAuto", Lang["Auto"])
    G_G.Call()
    _Gui.Add("Button", "x+15 w" w " hp -Wrap vRepR", Lang["RepR"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutR", Lang["CutR"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutR3", Lang["CutR3"])
    G_G.Call()
    _Gui.Add("Button", "xm+" i " wp hp -Wrap vRepD", Lang["RepD"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutD", Lang["CutD"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp hp -Wrap vCutD3", Lang["CutD3"])
    G_G.Call()
	;--------------
    Tab:=_Gui.Add("Tab3", "ys -Wrap", StrSplit(Lang["s2"],"|"))
    Tab.UseTab(1)
    _Gui.Add("Text", "x+30 y+35", Lang["Threshold"])
    _Gui.Add("Edit", "x+15 w100 vThreshold")
    _Gui.Add("Button", "x+15 yp-3 vGray2Two", Lang["Gray2Two"])
    G_G.Call()
    Tab.UseTab(2)
    _Gui.Add("Text", "x+30 y+35", Lang["GrayDiff"])
    _Gui.Add("Edit", "x+15 w100 vGrayDiff", "50")
    _Gui.Add("Button", "x+15 yp-3 vGrayDiff2Two", Lang["GrayDiff2Two"])
    G_G.Call()
    Tab.UseTab(3)
    _Gui.Add("Text", "x+10 y+15 Section", Lang["Similar1"])
    _Gui.Add("Edit", "x+5 w80 vSimilar1 Limit3")
    _Gui.Add("UpDown", "vSim Range0-100", 90)
    _Gui.Add("Button", "x+10 ys-2 vAddColorSim", Lang["AddColorSim"])
    G_G.Call()
    _Gui.Add("Text", "x+20 ys+4", Lang["DiffRGB2"])
    _Gui.Add("Edit", "x+5 ys w80 vDiffRGB2 Limit3")
    _Gui.Add("UpDown", "vdRGB2 Range0-255 Wrap", 16)
    _Gui.Add("Button", "x+10 ys-2 vAddColorDiff", Lang["AddColorDiff"])
    G_G.Call()
    _Gui.Add("Button", "xs vUndo2", Lang["Undo2"])
    G_G.Call()
    _Gui.Add("Edit", "x+10 yp+2 w340 vColorList")
    _Gui.Add("Button", "x+10 yp-2 vColor2Two", Lang["Color2Two"])
    G_G.Call()
    Tab.UseTab(4)
    _Gui.Add("Text", "x+30 y+35", Lang["Similar2"] " 0")
    _Gui.Add("Slider", "x+0 w120 vSimilar2 +Center Page1 NoTicks ToolTip", 90)
    _Gui.Add("Text", "x+0", "100")
    _Gui.Add("Button", "x+15 yp-3 vColorPos2Two", Lang["ColorPos2Two"])
    G_G.Call()
    Tab.UseTab(5)
    _Gui.Add("Text", "x+30 y+35", Lang["DiffRGB"])
    _Gui.Add("Edit", "x+5 w80 vDiffRGB Limit3")
    _Gui.Add("UpDown", "vdRGB Range0-255 Wrap", 16)
    _Gui.Add("Checkbox", "x+15 yp+5 vMultiColor", Lang["MultiColor"])
    G_G.Call()
    _Gui.Add("Button", "x+15 yp-5 vUndo", Lang["Undo"])
    G_G.Call()
    Tab.UseTab()
	;--------------
    _Gui.Add("Button", "xm vReset", Lang["Reset"])
    G_G.Call()
    _Gui.Add("Checkbox", "x+15 yp+5 vModify", Lang["Modify"])
    G_G.Call()
    _Gui.Add("Text", "x+30", Lang["Comment"])
    _Gui.Add("Edit", "x+5 yp-2 w250 vComment")
    _Gui.Add("Button", "x+10 yp-3 vSplitAdd", Lang["SplitAdd"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vAllAdd", Lang["AllAdd"])
    G_G.Call()
    _Gui.Add("Button", "x+30 wp vOK", Lang["OK"])
    G_G.Call()
    _Gui.Add("Button", "x+15 wp vCancel", Lang["Cancel"])
    G_G.Call()
    _Gui.Add("Button", "xm vBind0", Lang["Bind0"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind1", Lang["Bind1"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind2", Lang["Bind2"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind3", Lang["Bind3"])
    G_G.Call()
    _Gui.Add("Button", "x+10 vBind4", Lang["Bind4"])
    G_G.Call()
    _Gui.Add("Button", "x+30 vSavePic2", Lang["SavePic2"])
    G_G.Call()
    _Gui.Title:=Lang["s3"]
    _Gui.Show("Hide")
	;--------------------
    Try FindText_SubPic.Destroy()
    FindText_SubPic:=_Gui:=Gui()  ; Don't use +AlwaysOnTop
    _Gui.Opt("+Parent" parent_id " -Caption +ToolWindow -DPIScale")
    _Gui.MarginX:=0, _Gui.MarginY:=0
    _Gui.BackColor:="White"
    id:=_Gui.Add("Pic", "x0 y0 w100 h100"), sub_hpic:=id.Hwnd
    _Gui.Title:="SubPic"
    _Gui.Show("Hide")
    return
  Case "MakeMainWindow":
    Try FindText_Main.Destroy()
    FindText_Main:=_Gui:=Gui()
    _Gui.Opt("+LastFound +AlwaysOnTop -DPIScale")
    _Gui.MarginX:=15, _Gui.MarginY:=10
    _Gui.BackColor:=WindowColor
    _Gui.SetFont("s12", "Verdana")
    _Gui.Add("Text", "xm", Lang["NowHotkey"])
    _Gui.Add("Edit", "x+5 w160 vNowHotkey ReadOnly")
    _Gui.Add("Hotkey", "x+5 w160 vSetHotkey1")
    s:="F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12|LWin|Ctrl|Shift|Space|MButton"
      . "|ScrollLock|CapsLock|Ins|Esc|BS|Del|Tab|Home|End|PgUp|PgDn"
      . "|NumpadDot|NumpadSub|NumpadAdd|NumpadDiv|NumpadMult"
    _Gui.Add("DDL", "x+5 w160 vSetHotkey2", StrSplit(s,"|"))
    _Gui.Add("Button", "x+15 vApply", Lang["Apply"])
    G_G.Call()
    _Gui.Add("GroupBox", "xm y+0 w280 h55 vMyGroup cBlack")
    _Gui.Add("Text", "xp+15 yp+20 Section", Lang["Myww"] ": ")
    _Gui.Add("Text", "x+0 w80", nW//2)
    _Gui.Add("UpDown", "vMyww Range1-100", nW//2)
    _Gui.Add("Text", "x+15 ys", Lang["Myhh"] ": ")
    _Gui.Add("Text", "x+0 w80", nH//2)
    id:=_Gui.Add("UpDown", "vMyhh Range1-100", nH//2)
    id.GetPos(pX, pY, pW, pH)
    _Gui["MyGroup"].Move(,, pX+pW, pH+30)
    id:=_Gui.Add("Checkbox", "x+100 ys vAddFunc", Lang["AddFunc"] " FindText()")
    id.GetPos(pX, pY, pW, pH)
    pW:=pX+pW-15, pW:=(pW<720?720:pW), w:=pW//5
    _Gui.Add("Button", "xm y+18 w" w " vCutL2", Lang["CutL2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutR2", Lang["CutR2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutU2", Lang["CutU2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCutD2", Lang["CutD2"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vUpdate", Lang["Update"])
    G_G.Call()
    _Gui.SetFont("s6 bold", "Verdana")
    _Gui.Add("Edit", "xm y+10 w" pW " h260 vMyPic -Wrap HScroll")
    _Gui.SetFont("s12 norm", "Verdana")
    w:=pW//3
    _Gui.Add("Button", "xm w" w " vCapture", Lang["Capture"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vTest", Lang["Test"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vCopy", Lang["Copy"])
    G_G.Call()
    _Gui.Add("Button", "xm y+0 wp vCaptureS", Lang["CaptureS"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vGetRange", Lang["GetRange"])
    G_G.Call()
    _Gui.Add("Button", "x+0 wp vGetOffset", Lang["GetOffset"])
    G_G.Call()
    _Gui.Add("Edit", "xm y+10 w130 hp vClipText")
    _Gui.Add("Button", "x+0 vPaste", Lang["Paste"])
    G_G.Call()
    _Gui.Add("Button", "x+0 vTestClip", Lang["TestClip"])
    G_G.Call()
    id:=_Gui.Add("Button", "x+0 vGetClipOffset", Lang["GetClipOffset"])
    G_G.Call()
    id.GetPos(x,, w)
    w:=((pW+15)-(x+w))//2
    _Gui.Add("Edit", "x+0 w" w " hp vOffset")
    _Gui.Add("Button", "x+0 wp vCopyOffset", Lang["CopyOffset"])
    G_G.Call()
    _Gui.SetFont("cBlue")
    id:=_Gui.Add("Edit", "xm w" pW " h250 vscr -Wrap HScroll"), hscr:=id.Hwnd
    _Gui.Title:=Lang["s4"]
    _Gui.Show("Hide")
    G_.Call("LoadScr")
    OnExit(G_SaveScr)
    return
  Case "LoadScr":
    f:=A_Temp "\~scr1.tmp"
    FileRead, s, %f%
    FindText_Main["scr"].Value:=s
    return
  Case "SaveScr":
    f:=A_Temp "\~scr1.tmp"
    s:=FindText_Main["scr"].Value
    Try FileDelete, %f%
    FileAppend, %s%, %f%
    return
  Case "Capture", "CaptureS":
    _Gui:=FindText_Main
    if show_gui:=(WinExist("ahk_id" _Gui.Hwnd))
      this.Hide()
    if (cmd="Capture")
    {
      w:=_Gui["Myww"].Value
      h:=_Gui["Myhh"].Value
      p:=this.GetRange(w, h)
      sx:=p[1], sy:=p[2], sw:=p[3]-p[1]+1, sh:=p[4]-p[2]+1
		, Bind_ID:=p[5], bind_mode:=""
      _Gui:=FindText_Capture
      _Gui["MyTab1"].Choose(1)
    }
    else
    {
      sx:=0, sy:=0, sw:=1, sh:=1, Bind_ID:=WinExist("A"), bind_mode:=""
      _Gui:=FindText_Capture
      _Gui["MyTab1"].Choose(2)
    }
    n:=150000, x:=y:=-n, w:=h:=2*n
    hBM:=this.BitmapFromScreen(x,y,w,h,(arg1=0?0:1))
    G_.Call("CaptureUpdate")
    G_.Call("PicUpdate")
    FindText_SubPic.Show()
    Names:=[], s:=""
    Loop Files, % SavePicDir "*.bmp"
      Names.Push(v:=A_LoopFileFullPath), s.="|" RegExReplace(v,"i)^.*\\|\.bmp$")
    _Gui["SelectBox"].Delete()
    _Gui["SelectBox"].Add(StrSplit(Trim(s,"|"),"|"))
	;------------------------
    s:="SelGray|SelColor|SelR|SelG|SelB|Threshold|Comment|ColorList"
    Loop Parse, s, |
      _Gui[A_LoopField].Value:=""
    _Gui["Modify"].Value:=Modify:=0
    _Gui["MultiColor"].Value:=MultiColor:=0
    _Gui["GrayDiff"].Value:=50
    _Gui["Gray2Two"].Focus()
    _Gui["Gray2Two"].Opt("+Default")
    _Gui.Show("Center")
    Event:=Result:=""
    DetectHiddenWindows 0
    Critical, Off
    WinWaitClose % "ahk_id " _Gui.Hwnd
    Critical
    ToolTip
    FindText_SubPic.Hide()
    _Gui:=FindText_Main
    Clipboard:=Text:=RegExMatch(Result,"O)\|<[^>\n]*>[^$\n]+\$[^""\r\n]+",r)?r[0]:""
	;------------------------
    if (bind_mode!="")
    {
      WinGetTitle, tt, ahk_id %Bind_ID%
      WinGetClass, tc, ahk_id %Bind_ID%
      tt:=Trim(SubStr(tt,1,30) (tc ? " ahk_class " tc:""))
      tt:=StrReplace(RegExReplace(tt,"[;``]","``$0"),"""","""""")
      Result:="`nSetTitleMatchMode 2`nid:=WinExist(""" tt """)"
        . "`nFindText().BindWindow(id" (bind_mode=0 ? "":"," bind_mode)
        . ")  `; " Lang["s6"] " FindText().BindWindow(0)`n`n" Result
    }
    if (Event="OK")
    {
      s:=""
      if (!A_IsCompiled)
        Try FileRead, s, %A_LineFile%
      re:="Oi)\n\s*FindText[^\n]+args\*[\s\S]*"
      s:=RegExMatch(s, re, r) ? "`n;==========`n" r[0] "`n" : ""
      _Gui["scr"].Value:=Result "`n" s
      _Gui["MyPic"].Value:=Trim(this.ASCII(Result),"`n")
    }
    else if (Event="SplitAdd") || (Event="AllAdd")
    {
      s:=_Gui["scr"].Value
      r:=SubStr(s, 1, InStr(s,"=FindText("))
      i:=j:=0, re:="<[^>\n]*>[^$\n]+\$[^""\r\n]+"
      While j:=RegExMatch(r, re,, j+1)
        i:=InStr(r, "`n", 0, j)
      _Gui["scr"].Value:=SubStr(s,1,i) . Result . SubStr(s,i+1)
      _Gui["MyPic"].Value:=Trim(this.ASCII(Result),"`n")
    }
    if (Event) && RegExMatch(Result, "O)\$\d+\.[\w+/]{1,100}", r)
      this.EditScroll(hscr, "\Q" r[0] "\E")
    Event:=Result:=s:=""
	;----------------------
    if (show_gui && arg1="")
      G_Show.Call()
    return Text
  Case "CaptureUpdate":
    nX:=sx, nY:=sy, nW:=sw, nH:=sh
    bits:=this.GetBitsFromScreen(nX,nY,nW,nH,0,zx,zy)
    cors:=[], show:=[], ascii:=[]
		, SelPos:=bg:=color:=""
		, dx:=dy:=CutLeft:=CutRight:=CutUp:=CutDown:=0
    ListLines % (lls:=A_ListLines)?0:0
    if (nW>0 && nH>0 && bits.Scan0)
    {
      j:=bits.Stride-nW*4, p:=bits.Scan0+(nY-zy)*bits.Stride+(nX-zx)*4-j-4
      Loop % nH + 0*(k:=0)
      Loop % nW + 0*(p+=j)
        show[++k]:=1, cors[k]:=NumGet(0|p+=4,"uint")
    }
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    {
      c:=(++tx)<nW && ty<nH ? cors[ty*nW+tx+1] : WindowColor
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[++k]
    }
    Loop % 71 + 0*(k:=71*25)
      SendMessage,0x2001,0,0xAAFFFF,,% "ahk_id " C_[++k]
    ListLines % lls
    _Gui:=FindText_Capture
    _Gui["MySlider1"].Enabled:=nW>71
    _Gui["MySlider2"].Enabled:=nH>25
    _Gui["MySlider1"].Value:=0
    _Gui["MySlider2"].Value:=0
    return
  Case "PicUpdate":
    FindText_SubPic[sub_hpic].Value:="*w0 *h0 HBITMAP:" hBM
    _Gui:=FindText_Capture
    _Gui["MySlider3"].Value:=0
    _Gui["MySlider4"].Value:=0
    G_.Call("MySlider3")
    return
  Case "MySlider3", "MySlider4":
    _Gui:=FindText_Capture
    _Gui[parent_id].GetPos(,, w, h)
    MySlider3:=_Gui["MySlider3"].Value
    MySlider4:=_Gui["MySlider4"].Value
    FindText_SubPic[sub_hpic].GetPos(,, pW, pH)
    x:=pW>w ? -Round((pW-w)*MySlider3/100) : 0
    y:=pH>h ? -Round((pH-h)*MySlider4/100) : 0
    FindText_SubPic.Show("NA x" x " y" y " w" pW " h" pH)
    return
  Case "Reset":
    G_.Call("CaptureUpdate")
    return
  Case "LoadPic":
    FindText_Capture.Opt("+OwnDialogs")
    f:=arg1
    if (f="")
    {
      if !FileExist(SavePicDir)
        FileCreateDir, % SavePicDir
      f:=SavePicDir "*.bmp"
      Loop Files, % f
        f:=A_LoopFileFullPath
      FileSelectFile, f,, %f%, Select Picture
    }
    if !FileExist(f)
    {
      MsgBox, 4096, Tip, % Lang["s17"] " !", 1
      return
    }
    this.ShowPic(f, 0, sx, sy, sw, sh)
    hBM:=this.BitmapFromScreen(sx, sy, sw, sh, 0)
    sw:=Min(sw,200), sh:=Min(sh,200)
    G_.Call("CaptureUpdate")
    G_.Call("PicUpdate")
    return
  Case "SavePic":
    _Gui:=FindText_Capture
    SelectBox:=_Gui["SelectBox"].Value
    Try f:="", f:=Names[SelectBox]
    _Gui.Hide()
    this.ShowPic(f)
    Try GuiFromHwnd(WinExist("Show Pic")).Opt("+OwnDialogs")
    Loop
    {
      p:=this.GetRange2()
      MsgBox, 4100, Tip, % Lang["s15"] " !"
      IfMsgBox, Yes
        Break
    }
    G_.Call("ScreenShot", p[1] "|" p[2] "|" p[3] "|" p[4] "|0")
    this.ShowPic()
    return
  Case "SelectBox":
    SelectBox:=FindText_Capture["SelectBox"].Value
    Try f:="", f:=Names[SelectBox]
    if (f!="")
      G_.Call("LoadPic", f)
    return
  Case "ClearAll":
    FindText_Capture.Hide()
    FileDelete, % SavePicDir "*.bmp"
    return
  Case "OpenDir":
    FindText_Capture.Minimize()
    if !FileExist(SavePicDir)
      FileCreateDir, % SavePicDir
    Run, % SavePicDir
    return
  Case "GetRange":
    _Gui:=FindText_Main
    _Gui.Opt("+LastFound")
    this.Hide()
    p:=this.GetRange2(), v:=p[1] ", " p[2] ", " p[3] ", " p[4]
    s:=_Gui["scr"].Value
    re:="i)(=FindText\([^\n]*?)([^(,\n]*,){4}([^,\n]*,[^,\n]*,[^,\n]*Text)"
    if SubStr(s,1,s~="i)\n\s*FindText[^\n]+args\*")~=re
    {
      s:=RegExReplace(s, re, "$1 " v ",$3",, 1)
      _Gui["scr"].Value:=s
    }
    _Gui["Offset"].Value:=v
    G_Show.Call()
    return
  Case "Test", "TestClip":
    _Gui:=FindText_Main
    _Gui.Opt("+LastFound")
    this.Hide()
	;----------------------
    if (cmd="Test")
      s:=_Gui["scr"].Value
    else
      s:=_Gui["ClipText"].Value
    if (cmd="Test") && InStr(s, "MCode(")
    {
      s:="`n#NoEnv`nMenu, Tray, Click, 1`n" s "`nExitApp`n"
      Thread1:=new this.Thread(s)
      DetectHiddenWindows, 1
      WinWait, % "ahk_class AutoHotkey ahk_pid " Thread1.pid,, 3
      if (!ErrorLevel)
        WinWaitClose,,, 30
	; Thread1:=""  ; kill the Thread
    }
    else
    {
      t:=A_TickCount, v:=X:=Y:=""
      if RegExMatch(s, "O)<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
        v:=this.FindText(X, Y, 0,0,0,0, 0,0, r[0])
      r:=StrSplit(Lang["s8"] "||||", "|")
      MsgBox, 4096, Tip, % r[1] ":`t" (IsObject(v)?v.Length():v) "`n`n"
        . r[2] ":`t" (A_TickCount-t) " " r[3] "`n`n"
        . r[4] ":`t" X ", " Y "`n`n"
        . r[5] ":`t<" (IsObject(v)?v[1].id:"") ">", 3
      Try For i,j in v
        if (i<=2)
          this.MouseTip(j.x, j.y)
      v:="", Clipboard:=X "," Y
    }
	;----------------------
    G_Show.Call()
    return
  Case "GetOffset", "GetClipOffset":
    FindText_Main.Hide()
    p:=this.GetRange()
    _Gui:=FindText_Main
    if (cmd="GetOffset")
      s:=_Gui["scr"].Value
    else
      s:=_Gui["ClipText"].Value
    if RegExMatch(s, "O)<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
		&& this.FindText(X, Y, 0,0,0,0, 0,0, r[0])
    {
      r:=StrReplace("X+" ((p[1]+p[3])//2-X)
        . ", Y+" ((p[2]+p[4])//2-Y), "+-", "-")
      if (cmd="GetOffset")
      {
        re:="i)(\(\)\.\w*Click\w*\()[^,\n]*,[^,)\n]*"
        if SubStr(s,1,s~="i)\n\s*FindText[^\n]+args\*")~=re
          s:=RegExReplace(s, re, "$1" r,, 1)
        _Gui["scr"].Value:=s
      }
      _Gui["Offset"].Value:=r
    }
    s:="", G_Show.Call()
    return
  Case "Paste":
    if RegExMatch(Clipboard, "O)\|?<[^>\n]*>[^$\n]+\$[^""\r\n]+", r)
    {
      FindText_Main["ClipText"].Value:=r[0]
      FindText_Main["MyPic"].Value:=Trim(this.ASCII(r[0]),"`n")
    }
    return
  Case "CopyOffset":
    Clipboard:=FindText_Main["Offset"].Value
    return
  Case "Copy":
    ControlGet, s, Selected,,, ahk_id %hscr%
    if (s="")
    {
      s:=FindText_Main["scr"].Value
      r:=FindText_Main["AddFunc"].Value
      if (r != 1)
        s:=RegExReplace(s, "i)\n\s*FindText[^\n]+args\*[\s\S]*")
		, s:=RegExReplace(s, "i)\n; ok:=FindText[\s\S]*")
		, s:=SubStr(s, (s~="i)\n[ \t]*Text"))
    }
    Clipboard:=RegExReplace(s, "\R", "`r`n")
    ControlFocus,, % "ahk_id " hscr
    return
  Case "Apply":
    _Gui:=FindText_Main
    NowHotkey:=_Gui["NowHotkey"].Value
    SetHotkey1:=_Gui["SetHotkey1"].Value
    SetHotkey2:=_Gui["SetHotkey2"].Text
    if (NowHotkey!="")
      Hotkey, *%NowHotkey%,, Off UseErrorLevel
    k:=SetHotkey1!="" ? SetHotkey1 : SetHotkey2
    if (k!="")
      Hotkey, *%k%, %G_ScreenShot%, On UseErrorLevel
    _Gui["NowHotkey"].Value:=k
    _Gui["SetHotkey1"].Value:=""
    _Gui["SetHotkey2"].Choose(0)
    return
  Case "ScreenShot":
    Critical
    if !FileExist(SavePicDir)
      FileCreateDir, % SavePicDir
    Loop
      f:=SavePicDir . Format("{:03d}.bmp",A_Index)
    Until !FileExist(f)
    this.SavePic(f, StrSplit(arg1,"|")*)
    CoordMode, ToolTip
    this.ToolTip(Lang["s9"],, 0,, { bgcolor:"Yellow", color:"Red"
		, size:48, bold:"bold", trans:200, timeout:0.2 })
    return
  Case "Bind0", "Bind1", "Bind2", "Bind3", "Bind4":
    this.BindWindow(Bind_ID, bind_mode:=SubStr(cmd,5))
    n:=150000, x:=y:=-n, w:=h:=2*n
    hBM:=this.BitmapFromScreen(x,y,w,h,1)
    G_.Call("PicUpdate")
    FindText_Capture["MyTab1"].Choose(2)
    this.BindWindow(0)
    return
  Case "MySlider1", "MySlider2":
    SetTimer, %G_Slider%, -10
    return
  Case "Slider":
    Critical
    _Gui:=FindText_Capture
    MySlider1:=_Gui["MySlider1"].Value
    MySlider2:=_Gui["MySlider2"].Value
    dx:=nW>71 ? Round((nW-71)*MySlider1/100) : 0
    dy:=nH>25 ? Round((nH-25)*MySlider2/100) : 0
    if (oldx=dx && oldy=dy)
      return
    ListLines % (lls:=A_ListLines)?0:0
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    {
      c:=((++tx)>=nW || ty>=nH || !show[i:=ty*nW+tx+1]
      ? WindowColor : bg="" ? cors[i] : ascii[i] ? 0 : 0xFFFFFF)
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[++k]
    }
    Loop % 71*(oldx!=dx) + 0*(i:=nW*nH+dx)*(k:=71*25)
      SendMessage,0x2001,0,(show[++i]?0x0000FF:0xAAFFFF),,% "ahk_id " C_[++k]
    ListLines % lls
    oldx:=dx, oldy:=dy
    return
  Case "RepColor", "CutColor":
    if (cmd="RepColor")
      show[k]:=1, c:=(bg="" ? cors[k] : ascii[k] ? 0 : 0xFFFFFF)
    else
      show[k]:=0, c:=WindowColor
    if (tx:=Mod(k-1,nW)-dx)>=0 && tx<71 && (ty:=(k-1)//nW-dy)>=0 && ty<25
      SendMessage,0x2001,0,(c&0xFF)<<16|c&0xFF00|(c>>16)&0xFF,,% "ahk_id " C_[ty*71+tx+1]
    return
  Case "RepL":
    if (CutLeft<=0) || (bg!="" && InStr(color,"**") && CutLeft=1)
      return
    k:=CutLeft-nW, CutLeft--
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("RepColor"))
    return
  Case "CutL":
    if (CutLeft+CutRight>=nW)
      return
    CutLeft++, k:=CutLeft-nW
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("CutColor"))
    return
  Case "CutL3":
    Loop 3
      G_.Call("CutL")
    return
  Case "RepR":
    if (CutRight<=0) || (bg!="" && InStr(color,"**") && CutRight=1)
      return
    k:=1-CutRight, CutRight--
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("RepColor"))
    return
  Case "CutR":
    if (CutLeft+CutRight>=nW)
      return
    CutRight++, k:=1-CutRight
    Loop % nH
      k+=nW, (A_Index>CutUp && A_Index<nH+1-CutDown && G_.Call("CutColor"))
    return
  Case "CutR3":
    Loop 3
      G_.Call("CutR")
    return
  Case "RepU":
    if (CutUp<=0) || (bg!="" && InStr(color,"**") && CutUp=1)
      return
    k:=(CutUp-1)*nW, CutUp--
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("RepColor"))
    return
  Case "CutU":
    if (CutUp+CutDown>=nH)
      return
    CutUp++, k:=(CutUp-1)*nW
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("CutColor"))
    return
  Case "CutU3":
    Loop 3
      G_.Call("CutU")
    return
  Case "RepD":
    if (CutDown<=0) || (bg!="" && InStr(color,"**") && CutDown=1)
      return
    k:=(nH-CutDown)*nW, CutDown--
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("RepColor"))
    return
  Case "CutD":
    if (CutUp+CutDown>=nH)
      return
    CutDown++, k:=(nH-CutDown)*nW
    Loop % nW
      k++, (A_Index>CutLeft && A_Index<nW+1-CutRight && G_.Call("CutColor"))
    return
  Case "CutD3":
    Loop 3
      G_.Call("CutD")
    return
  Case "Gray2Two":
    ListLines % (lls:=A_ListLines)?0:0
    gs:=[], k:=0
    Loop % nW*nH
      gs[++k]:=((((c:=cors[k])>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
    _Gui:=FindText_Capture
    _Gui["Threshold"].Focus()
    Threshold:=_Gui["Threshold"].Value
    if (Threshold="")
    {
      pp:=[]
      Loop 256
        pp[A_Index-1]:=0
      Loop % nW*nH
        if (show[A_Index])
          pp[gs[A_Index]]++
      IP0:=IS0:=0
      Loop 256
        k:=A_Index-1, IP0+=k*pp[k], IS0+=pp[k]
      Threshold:=Floor(IP0/IS0)
      Loop 20
      {
        LastThreshold:=Threshold
        IP1:=IS1:=0
        Loop % LastThreshold+1
          k:=A_Index-1, IP1+=k*pp[k], IS1+=pp[k]
        IP2:=IP0-IP1, IS2:=IS0-IS1
        if (IS1!=0 && IS2!=0)
          Threshold:=Floor((IP1/IS1+IP2/IS2)/2)
        if (Threshold=LastThreshold)
          Break
      }
      _Gui["Threshold"].Value:=Threshold
    }
    Threshold:=Round(Threshold)
    color:="*" Threshold, k:=i:=0
    Loop % nW*nH
      ascii[++k]:=v:=(gs[k]<=Threshold)
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "GrayDiff2Two":
    _Gui:=FindText_Capture
    GrayDiff:=_Gui["GrayDiff"].Value
    if (GrayDiff="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s11"] " !", 1
      return
    }
    ListLines % (lls:=A_ListLines)?0:0
    gs:=[], k:=0
    Loop % nW*nH
      gs[++k]:=((((c:=cors[k])>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
    if (CutLeft=0)
      G_.Call("CutL")
    if (CutRight=0)
      G_.Call("CutR")
    if (CutUp=0)
      G_.Call("CutU")
    if (CutDown=0)
      G_.Call("CutD")
    GrayDiff:=Round(GrayDiff)
    color:="**" GrayDiff, k:=i:=0
    Loop % nW*nH
      j:=gs[++k]+GrayDiff
		, ascii[k]:=v:=( gs[k-1]>j || gs[k+1]>j
		|| gs[k-nW]>j || gs[k+nW]>j
		|| gs[k-nW-1]>j || gs[k-nW+1]>j
		|| gs[k+nW-1]>j || gs[k+nW+1]>j )
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "AddColorSim", "AddColorDiff":
    _Gui:=FindText_Capture
    c:=_Gui["SelColor"].Value
    if (c="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s12"] " !", 1
      return
    }
    s:=_Gui["ColorList"].Value, c:=StrReplace(c,"0x")
    if InStr(cmd, "Sim")
      v:=_Gui["Sim"].Value, v:=c "@" Round(v/100,2)
		, s:=RegExReplace("/" s, "/" c "@[^/]*") . "/" v
    else
      v:=_Gui["dRGB2"].Value, v:=c "-" Format("{:06X}",v<<16|v<<8|v)
		, s:=RegExReplace("/" s, "/" c "-[^/]*") . "/" v
    _Gui["ColorList"].Value:=Trim(s, "/")
    ControlSend,, {End}, % "ahk_id " _Gui["ColorList"].Hwnd
    G_.Call("Color2Two")
    return
  Case "Undo2":
    _Gui:=FindText_Capture
    s:=_Gui["ColorList"].Value
    s:=RegExReplace("/" s, "/[^/]+$")
    _Gui["ColorList"].Value:=Trim(s, "/")
    ControlSend,, {End}, % "ahk_id " _Gui["ColorList"].Hwnd
    return
  Case "Color2Two":
    _Gui:=FindText_Capture
    color:=Trim(_Gui["ColorList"].Value, "/")
    if (color="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s16"] " !", 1
      return
    }
    ListLines % (lls:=A_ListLines)?0:0
    k:=i:=v:=0, arr:=StrSplit(color, "/")
    Loop % nW*nH
    {
      c:=cors[++k], rr:=(c>>16)&0xFF, gg:=(c>>8)&0xFF, bb:=c&0xFF
      For k1,v1 in arr
      {
        c:=this.ToRGB(StrSplit(StrReplace(v1,"@","-") "-","-")[1])
		, r:=((c>>16)&0xFF)-rr, g:=((c>>8)&0xFF)-gg, b:=(c&0xFF)-bb
        if j:=InStr(v1, "@")
        {
          n:=this.Floor(SubStr(v1,j+1))
		, n:=Floor(4606*255*255*(1-n)*(1-n)), j:=r+rr+rr
          if v:=((1024+j)*r*r+2048*g*g+(1534-j)*b*b<=n)
            Break
        }
        else
        {
          c:=this.Floor("0x" StrSplit(v1 "-","-")[2])
		, dR:=(c>>16)&0xFF, dG:=(c>>8)&0xFF, dB:=c&0xFF
          if v:=(Abs(r)<=dR && Abs(g)<=dG && Abs(b)<=dB)
            Break
        }
      }
      ascii[k]:=v, (show[k] && i:=(v?i+1:i-1))
    }
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "ColorPos2Two":
    _Gui:=FindText_Capture
    c:=_Gui["SelColor"].Value
    if (c="")
    {
      _Gui.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s12"] " !", 1
      return
    }
    n:=this.Floor(_Gui["Similar2"].Value), n:=Round(n/100,2)
		, color:="#" c "@" n
		, n:=Floor(4606*255*255*(1-n)*(1-n)), k:=i:=0
		, rr:=(c>>16)&0xFF, gg:=(c>>8)&0xFF, bb:=c&0xFF
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nW*nH
      c:=cors[++k], r:=((c>>16)&0xFF)-rr
		, g:=((c>>8)&0xFF)-gg, b:=(c&0xFF)-bb, j:=r+rr+rr
		, ascii[k]:=v:=((1024+j)*r*r+2048*g*g+(1534-j)*b*b<=n)
		, (show[k] && i:=(v?i+1:i-1))
    bg:=(i>0 ? "1":"0"), G_.Call("BlackWhite")
    ListLines % lls
    return
  Case "BlackWhite":
    Loop % 25 + 0*(ty:=dy-1)*(k:=0)
    Loop % 71 + 0*(tx:=dx-1)*(ty++)
    if (k++)*0 + (++tx)<nW && ty<nH && show[i:=ty*nW+tx+1]
      SendMessage,0x2001,0,(ascii[i] ? 0 : 0xFFFFFF),,% "ahk_id " C_[k]
    return
  Case "Modify":
    Modify:=FindText_Capture["Modify"].Value
    return
  Case "MultiColor":
    MultiColor:=FindText_Capture["MultiColor"].Value
    Result:=""
    ToolTip
    return
  Case "Undo":
    Result:=RegExReplace(Result, ",[^/]+/[^/]+/[^/]+$")
    ToolTip % Trim(Result,"/,")
    return
  Case "GetTxt":
    txt:=""
    if (bg="")
      return
    k:=0
    ListLines % (lls:=A_ListLines)?0:0
    Loop % nH
    {
      v:=""
      Loop % nW
        v.=!show[++k] ? "" : ascii[k] ? "1":"0"
      txt.=v="" ? "" : v "`n"
    }
    ListLines % lls
    return
  Case "Auto":
    G_.Call("GetTxt")
    if (txt="")
    {
      FindText_Capture.Opt("+OwnDialogs")
      MsgBox, 4096, Tip, % Lang["s13"] " !", 1
      return
    }
    While InStr(txt,bg)
    {
      if (txt~="^" bg "+\n")
        txt:=RegExReplace(txt, "^" bg "+\n"), G_.Call("CutU")
      else if !(txt~="m`n)[^\n" bg "]$")
        txt:=RegExReplace(txt, "m`n)" bg "$"), G_.Call("CutR")
      else if (txt~="\n" bg "+\n$")
        txt:=RegExReplace(txt, "\n\K" bg "+\n$"), G_.Call("CutD")
      else if !(txt~="m`n)^[^\n" bg "]")
        txt:=RegExReplace(txt, "m`n)^" bg), G_.Call("CutL")
      else Break
    }
    txt:=""
    return
  Case "OK", "SplitAdd", "AllAdd":
    _Gui:=FindText_Capture
    _Gui.Opt("+OwnDialogs")
    G_.Call("GetTxt")
    if (txt="") && (!MultiColor)
    {
      MsgBox, 4096, Tip, % Lang["s13"] " !", 1
      return
    }
    if InStr(color,"#") && (!MultiColor)
    {
      r:=StrSplit(color,"@","#")
      k:=i:=j:=0
      ListLines % (lls:=A_ListLines)?0:0
      Loop % nW*nH
      {
        if (!show[++k])
          Continue
        i++
        if (k=SelPos)
        {
          j:=i
          Break
        }
      }
      ListLines % lls
      if (j=0)
      {
        MsgBox, 4096, Tip, % Lang["s12"] " !", 1
        return
      }
      color:="#" j "@" r[2]
    }
    Comment:=_Gui["Comment"].Value
    if (cmd="SplitAdd") && (!MultiColor)
    {
      if InStr(color,"#")
      {
        MsgBox, 4096, Tip, % Lang["s14"], 3
        return
      }
      bg:=StrLen(StrReplace(txt,"0"))
        > StrLen(StrReplace(txt,"1")) ? "1":"0"
      s:="", i:=0, k:=nW*nH+1+CutLeft
      Loop % w:=nW-CutLeft-CutRight
      {
        i++
        if (!show[k++] && A_Index<w)
          Continue
        i:=Format("{:d}",i)
        v:=RegExReplace(txt,"m`n)^(.{" i "}).*","$1")
        txt:=RegExReplace(txt,"m`n)^.{" i "}"), i:=0
        While InStr(v,bg)
        {
          if (v~="^" bg "+\n")
            v:=RegExReplace(v,"^" bg "+\n")
          else if !(v~="m`n)[^\n" bg "]$")
            v:=RegExReplace(v,"m`n)" bg "$")
          else if (v~="\n" bg "+\n$")
            v:=RegExReplace(v,"\n\K" bg "+\n$")
          else if !(v~="m`n)^[^\n" bg "]")
            v:=RegExReplace(v,"m`n)^" bg)
          else Break
        }
        if (v!="")
        {
          v:=Format("{:d}",InStr(v,"`n")-1) "." this.bit2base64(v)
          s.="`nText.=""|<" SubStr(Comment,1,1) ">" color "$" v """`n"
          Comment:=SubStr(Comment, 2)
        }
      }
      Event:=cmd, Result:=s
      _Gui.Hide()
      return
    }
    if (!MultiColor)
      txt:=Format("{:d}",InStr(txt,"`n")-1) "." this.bit2base64(txt)
    else
    {
      n:=_Gui["dRGB"].Value
      color:=Format("##{:06X}", n<<16|n<<8|n)
      r:=StrSplit(Trim(StrReplace(Result, ",", "/"), "/"), "/")
		, x:=r[1], y:=r[2], s:="", i:=1
      SetFormat, IntegerFast, d
      Loop % r.Length()//3
        s.="," (r[i++]-x) "/" (r[i++]-y) "/" r[i++]
      txt:=SubStr(s,2)
    }
    s:="`nText.=""|<" Comment ">" color "$" txt """`n"
    if (cmd="AllAdd")
    {
      Event:=cmd, Result:=s
      _Gui.Hide()
      return
    }
    x:=nX+CutLeft+(nW-CutLeft-CutRight)//2
    y:=nY+CutUp+(nH-CutUp-CutDown)//2
    s:=StrReplace(s, "Text.=", "Text:="), r:=StrSplit(Lang["s8"] "|||||||", "|")
    s:="`; #Include <FindText>`n"
    . "`nt1:=A_TickCount, Text:=X:=Y:=""""`n" s
    . "`nif (ok:=FindText(X, Y, " x "-150000, "
    . y "-150000, " x "+150000, " y "+150000, 0, 0, Text))"
    . "`n{"
    . "`n  `; FindText()." . "Click(" . "X, Y, ""L"")"
    . "`n}`n"
    . "`n`; ok:=FindText(X:=""wait"", Y:=3, 0,0,0,0,0,0,Text)    `; " r[7]
    . "`n`; ok:=FindText(X:=""wait0"", Y:=-1, 0,0,0,0,0,0,Text)  `; " r[8]
    . "`n`nMsgBox, 4096, Tip, `% """ r[1] ":``t"" (IsObject(ok)?ok.Length():ok)"
    . "`n  . ""``n``n" r[2] ":``t"" (A_TickCount-t1) "" " r[3] """"
    . "`n  . ""``n``n" r[4] ":``t"" X "", "" Y"
    . "`n  . ""``n``n" r[5] ":``t<"" (IsObject(ok)?ok[1].id:"""") "">""`n"
    . "`nTry For i,v in ok  `; ok " r[6] " ok:=FindText().ok"
    . "`n  if (i<=2)"
    . "`n    FindText().MouseTip(ok[i].x, ok[i].y)`n"
    Event:=cmd, Result:=s
    _Gui.Hide()
    return
  Case "SavePic2":
    x:=nX+CutLeft, w:=nW-CutLeft-CutRight
    y:=nY+CutUp, h:=nH-CutUp-CutDown
    G_.Call("ScreenShot", x "|" y "|" (x+w-1) "|" (y+h-1) "|0")
    return
  Case "ShowPic":
    ControlGet, i, CurrentLine,,, ahk_id %hscr%
    ControlGet, s, Line, %i%,, ahk_id %hscr%
    FindText_Main["MyPic"].Value:=Trim(this.ASCII(s),"`n")
    return
  Case "KeyDown":
    Critical
    _Gui:=FindText_Main
    if (WinExist()!=_Gui.Hwnd)
      return
    Try ctrl:="", ctrl:=args[3]
    if (ctrl=hscr)
      SetTimer, %G_ShowPic%, -150
    else if (ctrl=_Gui["ClipText"].Hwnd)
    {
      s:=_Gui["ClipText"].Value
      _Gui["MyPic"].Value:=Trim(this.ASCII(s),"`n")
    }
    return
  Case "LButtonDown":
    Critical
    Try k1:="", k1:=GuiFromHwnd(args[3],1).Hwnd
    if (k1=FindText_SubPic.Hwnd)
    {
	; Two windows trigger two messages
      if (A_TickCount-oldt)<100 || !this.State("LButton")
        return
      CoordMode, Mouse
      MouseGetPos, k1, k2
      ListLines % (lls:=A_ListLines)?0:0
      Loop
      {
        Sleep 50
        MouseGetPos, k3, k4
        this.RangeTip(Min(k1,k3), Min(k2,k4)
		, Abs(k1-k3)+1, Abs(k2-k4)+1, (A_MSec<500 ? "Red":"Blue"))
      }
      Until !this.State("LButton")
      ListLines % lls
      this.RangeTip()
      this.GetBitsFromScreen(,,,,0,zx,zy)
      this.ClientToScreen(sx, sy, 0, 0, sub_hpic)
      if Abs(k1-k3)+Abs(k2-k4)>4
        sx:=zx+Min(k1,k3)-sx, sy:=zy+Min(k2,k4)-sy
		, sw:=Abs(k1-k3)+1, sh:=Abs(k2-k4)+1
      else
        sx:=zx+k1-sx-71//2, sy:=zy+k2-sy-25//2, sw:=71, sh:=25
      G_.Call("CaptureUpdate")
      FindText_Capture["MyTab1"].Choose(1)
      oldt:=A_TickCount
      return
    }
    else if (k1!=FindText_Capture.Hwnd)
      return G_.Call("KeyDown", arg1, args*)
    MouseGetPos,,,, k2, 2
    k1:=0
    ListLines % (lls:=A_ListLines)?0:0
    For k_,v_ in C_
      if (v_=k2) && (k1:=k_)
        Break
    ListLines % lls
    if (k1<1)
      return
    else if (k1>71*25)
    {
      k3:=nW*nH+dx+(k1-71*25)
      SendMessage,0x2001,0,((show[k3]:=!show[k3])?0x0000FF:0xAAFFFF),,% "ahk_id " k2
      return
    }
    k3:=Mod(k1-1,71)+dx, k4:=(k1-1)//71+dy
    if (k3>=nW || k4>=nH)
      return
    k1:=k, k:=k4*nW+k3+1, k5:=c
    if (MultiColor && show[k])
    {
      c:="," (nX+k3) "/" (nY+k4) "/" Format("{:06X}",cors[k]&0xFFFFFF)
		, Result.=InStr(Result,c) ? "":c
      ToolTip % Trim(Result,"/,")
    }
    if (Modify && bg!="" && show[k])
    {
      c:=((ascii[k]:=!ascii[k]) ? 0 : 0xFFFFFF)
      SendMessage,0x2001,0,c,,% "ahk_id " k2
    }
    else
    {
      c:=cors[k], SelPos:=k
      _Gui:=FindText_Capture
      _Gui["SelGray"].Value:=(((c>>16)&0xFF)*38+((c>>8)&0xFF)*75+(c&0xFF)*15)>>7
      _Gui["SelColor"].Value:=Format("0x{:06X}",c&0xFFFFFF)
      _Gui["SelR"].Value:=(c>>16)&0xFF
      _Gui["SelG"].Value:=(c>>8)&0xFF
      _Gui["SelB"].Value:=c&0xFF
    }
    k:=k1, c:=k5
    return
  Case "RButtonDown":
    Critical
    Try k1:="", k1:=GuiFromHwnd(args[3],1).Hwnd
    if (k1!=FindText_SubPic.Hwnd)
      return
	; Two windows trigger two messages
    if (A_TickCount-oldt)<100 || !this.State("RButton")
      return
    r:=[x, y, w, h, pX, pY, pW, pH]
    CoordMode, Mouse
    MouseGetPos, k1, k2
    WinGetPos, x, y, w, h, ahk_id %parent_id%
    WinGetPos, pX, pY, pW, pH, ahk_id %sub_hpic%
    pX-=x, pY-=y, pW-=w, pH-=h
    ListLines % (lls:=A_ListLines)?0:0
    Loop
    {
      Sleep 10
      MouseGetPos, k3, k4
      x:=Min(Max(pX+k3-k1,-pW),0), y:=Min(Max(pY+k4-k2,-pH),0)
      FindText_SubPic.Show("NA x" x " y" y)
      FindText_Capture["MySlider3"].Value:=Round(-x/pW*100)
      FindText_Capture["MySlider4"].Value:=Round(-y/pH*100)
    }
    Until !this.State("RButton")
    ListLines % lls
    x:=r[1], y:=r[2], w:=r[3], h:=r[4], pX:=r[5], pY:=r[6], pW:=r[7], pH:=r[8]
    oldt:=A_TickCount
    return
  Case "MouseMove":
    Try ctrl_name:="", ctrl_name:=GuiCtrlFromHwnd(args[3]).Name
    if (PrevControl != ctrl_name)
    {
      ToolTip
      PrevControl:=ctrl_name
      if (G_ToolTip)
      {
        SetTimer, %G_ToolTip%, % PrevControl ? -500 : "Off"
        SetTimer, %G_ToolTipOff%, % PrevControl ? -5500 : "Off"
      }
    }
    return
  Case "ToolTip":
    MouseGetPos,,, _TT
    if WinExist("ahk_id " _TT " ahk_class AutoHotkeyGUI")
      ToolTip % Tip_Text[PrevControl]
    return
  Case "ToolTipOff":
    ToolTip
    return
  Case "CutL2", "CutR2", "CutU2", "CutD2":
    s:=FindText_Main["MyPic"].Value
    s:=Trim(s,"`n") . "`n", v:=SubStr(cmd,4,1)
    if (v="U")
      s:=RegExReplace(s,"^[^\n]+\n")
    else if (v="D")
      s:=RegExReplace(s,"[^\n]+\n$")
    else if (v="L")
      s:=RegExReplace(s,"m`n)^[^\n]")
    else if (v="R")
      s:=RegExReplace(s,"m`n)[^\n]$")
    FindText_Main["MyPic"].Value:=Trim(s,"`n")
    return
  Case "Update":
    ControlFocus,, % "ahk_id " hscr
    ControlGet, i, CurrentLine,,, ahk_id %hscr%
    ControlGet, s, Line, %i%,, ahk_id %hscr%
    if !RegExMatch(s, "O)(<[^>\n]*>[^$\n]+\$)\d+\.[\w+/]+", r)
      return
    v:=FindText_Main["MyPic"].Value
    v:=Trim(v,"`n") . "`n", w:=Format("{:d}",InStr(v,"`n")-1)
    v:=StrReplace(StrReplace(v,"0","1"),"_","0")
    s:=StrReplace(s, r[0], r[1] . w "." this.bit2base64(v))
    v:="{End}{Shift Down}{Home}{Shift Up}{Del}"
    ControlSend,, %v%, ahk_id %hscr%
    Control, EditPaste, %s%,, ahk_id %hscr%
    ControlSend,, {Home}, ahk_id %hscr%
    return
  }
}

Lang(text:="", getLang:=0)
{
  local
  static init, Lang1, Lang2
  if !VarSetCapacity(init) && (init:="1")
  {
    s:="
    (
Myww       = 宽度 = 调整抓图范围的宽度
Myhh       = 高度 = 调整抓图范围的高度
AddFunc    = 附加 = 复制时带 FindText() 函数
NowHotkey  = 截屏热键 = 当前的截屏热键
SetHotkey1 = = 第一优先级的截屏热键
SetHotkey2 = = 第二优先级的截屏热键
Apply      = 应用 = 应用新的截屏热键
CutU2      = 上删 = 裁剪下面编辑框中文字的上边缘
CutL2      = 左删 = 裁剪下面编辑框中文字的左边缘
CutR2      = 右删 = 裁剪下面编辑框中文字的右边缘
CutD2      = 下删 = 裁剪下面编辑框中文字的下边缘
Update     = 更新 = 更新下面编辑框中文字到代码行中
GetRange   = 获取屏幕范围 = 获取屏幕范围到剪贴板并替换代码中的范围参数
GetOffset  = 获取相对坐标 = 获取相对图像位置的偏移坐标并替换代码中的点击坐标
GetClipOffset  = 获取相对坐标2 = 获取相对左边编辑框的图像的偏移坐标
Capture    = 抓图 = 开始屏幕抓图
CaptureS   = 截屏抓图 = 先截屏,然后显示截屏图像,再手动选择图像内的范围抓图
Test       = 测试 = 测试生成的代码是否可以查找成功
TestClip   = 测试2 = 测试左边文本框中的文字是否可以查找成功,结果复制到剪贴板
Paste      = 粘贴 = 粘贴剪贴板的文字数据
CopyOffset = 复制2 = 复制左边的偏移坐标到剪贴板
Copy       = 复制 = 复制代码到剪贴板
Reset      = 重读 = 重新读取原来的彩色图像
SplitAdd   = 分割添加 = 点击黄色的标签来分割图像为多个图像数据,添加到旧代码中
AllAdd     = 整体添加 = 将文字数据整体添加到旧代码中
Gray2Two      = 灰度阈值二值化 = 灰度小于阈值的为黑色其余白色
GrayDiff2Two  = 灰度差值二值化 = 某点与周围灰度之差大于差值的为黑色其余白色
Color2Two     = 颜色二值化 = 通过颜色列表来转换图像为黑白图
ColorPos2Two  = 颜色位置二值化 = 指定颜色及相似色为黑色其余白色,但是记录该色的位置
SelGray    = 灰度 = 选定颜色的灰度值 (0-255)
SelColor   = 颜色 = 选定颜色的RGB颜色值
SelR       = 红 = 选定颜色的红色分量
SelG       = 绿 = 选定颜色的绿色分量
SelB       = 蓝 = 选定颜色的蓝色分量
RepU       = -上 = 撤销裁剪上边缘1个像素
CutU       = 上 = 裁剪上边缘1个像素
CutU3      = 上3 = 裁剪上边缘3个像素
RepL       = -左 = 撤销裁剪左边缘1个像素
CutL       = 左 = 裁剪左边缘1个像素
CutL3      = 左3 = 裁剪左边缘3个像素
Auto       = 自动 = 二值化之后自动裁剪空白边缘
RepR       = -右 = 撤销裁剪右边缘1个像素
CutR       = 右 = 裁剪右边缘1个像素
CutR3      = 右3 = 裁剪右边缘3个像素
RepD       = -下 = 撤销裁剪下边缘1个像素
CutD       = 下 = 裁剪下边缘1个像素
CutD3      = 下3 = 裁剪下边缘3个像素
Modify     = 修改 = 二值化后可以用鼠标在预览区点击手动修改黑白点
MultiColor = 多色查找 = 鼠标选择多种颜色,之后点击“确定”按钮
Undo       = 撤销 = 撤销上一次选择的颜色
Undo2      = 撤销 = 撤销上一次添加到颜色列表的颜色
Comment    = 识别文字 = 识别文本 (包含在<>中),分割添加时也会分解成单个文字
Threshold  = 灰度阈值 = 灰度阈值 (0-255)
GrayDiff   = 灰度差值 = 灰度差值 (0-255)
Similar1   = 相似度 = 与选定颜色的相似度
Similar2   = 相似度 = 与选定颜色的相似度
AddColorSim  = 添加列表 = 颜色相似模式添加到颜色列表中再运行颜色二值化
AddColorDiff = 添加列表 = 颜色偏色模式添加到颜色列表中再运行颜色二值化
ColorList  = = 颜色列表用于转换图像为二值图
DiffRGB    = 红/绿/蓝 = 多色查找时各分量允许的偏差 (0-255)
DiffRGB2   = 红/绿/蓝 = 多色查找时各分量允许的偏差 (0-255)
Bind0      = 绑定窗口1 = 绑定窗口使用GetDCEx()获取后台窗口图像
Bind1      = 绑定窗口1+ = 绑定窗口使用GetDCEx()并修改窗口透明度
Bind2      = 绑定窗口2 = 绑定窗口使用PrintWindow()获取后台窗口图像
Bind3      = 绑定窗口2+ = 绑定窗口使用PrintWindow()并修改窗口透明度
Bind4      = 绑定窗口3 = 绑定窗口使用PrintWindow(,,3)获取后台窗口图像
OK         = 确定 = 生成全新的代码替换旧代码
OK2        = 确定 = 恢复截屏到屏幕然后再抓图
Cancel     = 取消 = 关闭窗口不做任何事
Cancel2    = 取消 = 关闭窗口不做任何事
ClearAll   = 清空 = 清空所有保存的截图
OpenDir    = 打开目录 = 打开保存屏幕截图的目录
SavePic    = 保存图片 = 选择一个范围保存为图片
SavePic2   = 保存图片 = 将修剪后的原始图像保存为图片
LoadPic    = 载入图片 = 载入一张图片作为抓取的图像
ClipText   = = 显示粘贴的文字数据
Offset     = = 显示“获取相对坐标2”或者“获取屏幕范围”的结果
SelectBox  = = 选择截图显示到屏幕左上角
s1  = FindText
s2  = 灰度阈值|灰度差值|颜色|颜色位置|多色查找
s3  = 图像二值化及分割
s4  = 抓图生成字库及找字代码
s5  = 方向键微调选框\n先点击右键(Ctrl)一次\n把鼠标移开\n再点击右键(Ctrl)一次
s6  = 解绑窗口使用
s7  = 左键(Ctrl)拖动选择范围\n坐标复制到剪贴板
s8  = 找到|时间|毫秒|位置|结果|值可以这样获取|等待3秒等图像出现|无限等待等图像消失
s9  = 截屏成功
s10 = 鼠标位置|穿透显示绑定窗口\n点击右键完成抓图
s11 = 请先设定灰度差值
s12 = 请先选择核心颜色
s13 = 请先将图像二值化
s14 = 不能用于颜色位置二值化模式, 因为分割后会导致位置错误
s15 = 你确定选择的范围吗?\n\n如果不确定,可以重新选择
s16 = 请先添加颜色到颜色列表
s17 = 请先保存图片
s18 = 捕获|截图
	)"
	Lang1:=[], Lang2:=[]
	loop Parse, s, `n, `r
		if InStr(v:=A_LoopField, "=")
			r:=StrSplit(StrReplace(v "==","\n","`n"), "=", "`t ")
			, Lang1[r[1]]:=r[2], Lang2[r[1]]:=r[3]
		}
return getLang=1 ? Lang1 : getLang=2 ? Lang2 : Lang1[text]
}

}  ;// Class End


;---------------------------------
; Gui-V1-V2 兼容库  By FeiYue
;---------------------------------

Gui(args*) {
	return new GuiCreate(args*)
}

GuiFromHwnd(hwnd:="AllGuiObj", RecurseParent:=0) {
	static init, AllGuiObj
	if !VarSetCapacity(init) && (init:="1")
		AllGuiObj:=[]
	if (hwnd=="AllGuiObj")
		return AllGuiObj
	if (RecurseParent)
		while hwnd && !AllGuiObj.HasKey(hwnd)
			hwnd:=DllCall("GetParent", "Ptr",hwnd, "Ptr")
	return AllGuiObj[hwnd]
}

GuiCtrlFromHwnd(hwnd) {
	return GuiFromHwnd(hwnd,1)[hwnd]
}

GuiCreate_Close(args*) {
	return GuiCreate_G("Close", args*)
}

GuiCreate_ContextMenu(args*) {
	return GuiCreate_G("ContextMenu", args*)
}

GuiCreate_DropFiles(args*) {
	return GuiCreate_G("DropFiles", args*)
}

GuiCreate_Escape(args*) {
	return GuiCreate_G("Escape", args*)
}

GuiCreate_Size(args*) {
	return GuiCreate_G("Size", args*)
}

GuiCreate_G(EventName, args*) {
	local
	return (G:=GuiFromHwnd(WinExist()))["_" EventName].Call(G, args*)
}

class GuiCreate {

	__New(opts:="", title:="", args*) {
local
Gui, New, % opts " +Hwndhwnd +LabelGuiCreate_", % title
this.Hwnd:=hwnd, this.ClassNN:=[]
GuiFromHwnd()[hwnd]:=this
}

__Delete() {
	this.Destroy()
}

Destroy() {
	local
	if !(hwnd:=this.Hwnd)
		return
	this.Hwnd:="", GuiFromHwnd().Delete(hwnd)
	Try Gui, % hwnd ":Destroy"
		For k,v in this
		(v.Hwnd && v.Hwnd:=""), this[k]:=""
  }

  OnEvent(EventName, Callback, AddRemove:=1) {
    if IsObject(Callback)
      this["_" EventName]:=Callback
  }

  Opt(opts) {
    Gui, % this.Hwnd ":" RegExReplace(opts,"i)[+\-\s]Label\S*")
  }

  Add(type, opts:="", text:="") {
    local
    static init, type2class
    if !VarSetCapacity(init) && (init:="1")
      type2class:=[]
    type:=(type="DropDownList"?"DDL":type="Picture"?"Pic":type)
    name:=RegExMatch(opts, "i)(^|[+\-\s])V(?!Scroll\b|ertical\b)\K\S*", r)?r:""
    opts:=RegExReplace(opts, "i)(^|[+\-\s])V(?!Scroll\b|ertical\b)\S*")
    if IsObject(text)
    {
      s:=""
      For k,v in text
        s.="|" v
      text:=Trim(s, "|")
    }
    Gui, % this.Hwnd ":Add", % type, % opts " +Hwndhwnd", % text
    this.LastHwnd:=hwnd
    if type2class.HasKey(type)
      s:=type2class[type]
    else
    {
      WinGetClass, s, ahk_id %hwnd%
      type2class[type]:=s
    }
    this.ClassNN[s]:=n:=Floor(this.ClassNN[s])+1, classnn:=s . n
    obj:= new this.Control(this.Hwnd, hwnd, type, classnn, name)
    this[hwnd]:=obj, this[classnn]:=obj
    if (name) && !(name~="i)^(Destroy|OnEvent|Opt|Add"
    . "|SetFont|Show|Hide|Move|GetClientPos|GetPos|Maximize"
    . "|Minimize|Restore|Flash|Submit|Hwnd|Name|Title"
    . "|BackColor|MarginX|MarginY|MenuBar|FocusedCtrl)$")
      this[name]:=obj
    return obj
  }

  SetFont(opts:="", FontName:="") {
    Gui, % this.Hwnd ":Font", % opts, % FontName
  }

  Show(opts:="", args*) {
    Gui, % this.Hwnd ":Show", % opts
  }

  Hide() {
    Gui, % this.Hwnd ":Hide"
  }

  Move(x:="", y:="", w:="", h:="") {
    local
    this.GetPos(pX, pY, pW, pH)
    x:=(x=""?pX:x), y:=(y=""?pY:y), w:=(w=""?pW:w), h:=(h=""?pH:h)
    DllCall("MoveWindow", "Ptr",this.Hwnd, "int",x, "int",y, "int",w, "int",h, "int",1)
  }

  GetClientPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    VarSetCapacity(rect, 16, 0)
				, DllCall("GetClientRect",  "Ptr",this.Hwnd, "Ptr",&rect)
				, DllCall("ClientToScreen", "Ptr",this.Hwnd, "Ptr",&rect)
				, x:=NumGet(rect, 0, "int"), y:=NumGet(rect, 4, "int")
				, w:=NumGet(rect, 8, "int")-x, h:=NumGet(rect, 12, "int")-y
  }

  GetPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    VarSetCapacity(rect, 16, 0)
				, DllCall("GetWindowRect",  "Ptr",this.Hwnd, "Ptr",&rect)
				, x:=NumGet(rect, 0, "int"), y:=NumGet(rect, 4, "int")
				, w:=NumGet(rect, 8, "int")-x, h:=NumGet(rect, 12, "int")-y
  }

  Maximize() {
    Gui, % this.Hwnd ":Maximize"
  }

  Minimize() {
    Gui, % this.Hwnd ":Minimize"
  }

  Restore() {
    Gui, % this.Hwnd ":Restore"
  }

  Flash(k:=1) {
    Gui, % this.Hwnd ":Flash", % k ? "":"Off"
  }

  Submit(hide:=1) {
    local
    (hide && this.Hide()), arr:=[]
    For k,v in this
      if k is number
        if (v.Name!="")
          arr[v.Name]:=v.Value
    return arr
  }

  BackColor {
    get {
      return this._BackColor
    }
    set {
      this._BackColor:=value
      Gui, % this.Hwnd ":Color", % value
      return value
    }
  }

  MarginX {
    get {
      return this._MarginX
    }
    set {
      this._MarginX:=value
      Gui, % this.Hwnd ":Margin", % value
      return value
    }
  }

  MarginY {
    get {
      return this._MarginY
    }
    set {
      this._MarginY:=value
      Gui, % this.Hwnd ":Margin",, % value
      return value
    }
  }

  MenuBar {
    get {
      return this._MenuBar
    }
    set {
      this._MenuBar:=value
      Gui, % this.Hwnd ":Menu", % value
      return value
    }
  }

  Title {
    get {
      local
      VarSetCapacity(v, 260*2)
      DllCall("GetWindowText", "Ptr",this.Hwnd, "Str",v, "Int",260)
      return v
    }
    set {
      DllCall("SetWindowText", "Ptr",this.Hwnd, "Str",value)
      return value
    }
  }

  FocusedCtrl {
    get {
      local
      GuiControlGet, v, % this.Hwnd ":Focus"
      return this[v]
    }
  }

			;========  Sub Class =========

  Class Control {

  __New(GuiHwnd, hwnd, type, classnn, name) {
    this.GuiHwnd:=GuiHwnd, this.Hwnd:=hwnd
    this.Type:=type, this.ClassNN:=classnn, this.Name:=name
  }

  Opt(opts) {
    GuiControl, % opts, % this.Hwnd
  }

  OnEvent(EventName, Callback, AddRemove:=1) {
    local
    r:=this.OnEvent_G.Bind(this, Callback)
    GuiControl, +g, % this.Hwnd, % r
  }

  OnEvent_G(Callback, args*) {
    if IsObject(Callback)
      return %Callback%(this, args*)
  }

  GetPos(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="") {
    local
    GuiControlGet, p, Pos, % this.Hwnd
    x:=Floor(pX), y:=Floor(pY), w:=Floor(pW), h:=Floor(pH)
  }

  Move(x:="", y:="", w:="", h:="") {
    local
    s:=(x=""?"":" x" x) (y=""?"":" y" y) (w=""?"":" w" w) (h=""?"":" h" h)
    GuiControl, Move, % this.Hwnd, % s
  }

  Redraw() {
    GuiControl, MoveDraw, % this.Hwnd
  }

  Focus() {
    GuiControl, Focus, % this.Hwnd
  }

  UseTab(Name:="", Exact:="", index:="") {
    Gui, % this.GuiHwnd ":Tab", % Name, % index, % Exact?"Exact":""
  }

  SetFont(opts:="", FontName:="") {
    Gui, % this.GuiHwnd ":Font", % opts, % FontName
    GuiControl, Font, % this.Hwnd
  }

  Add(text) {
    local
    if IsObject(text)
    {
      s:=""
      For k,v in text
        s.="|" v
      text:=Trim(s, "|")
    }
    GuiControl,, % this.Hwnd, % text
  }

  Delete(N:="") {
    if (N="")
      GuiControl,, % this.Hwnd, |
    else
      this.Choose(N), this.Choose(0)
  }

  Choose(N) {
    if N is number
      GuiControl, Choose, % this.Hwnd, % N
    else
      GuiControl, ChooseString, % this.Hwnd, % N
  }

  Gui {
    get {
      return GuiFromHwnd(this.GuiHwnd)
    }
  }

  Enabled {
    get {
      local
      GuiControlGet, v, Enabled, % this.Hwnd
      return v
    }
    set {
      GuiControl, % "Enable" (!!value), % this.Hwnd
      return value
    }
  }

  Visible {
    get {
      local
      GuiControlGet, v, Visible, % this.Hwnd
      return v
    }
    set {
      GuiControl, % "Show" (!!value), % this.Hwnd
      return value
    }
  }

  Focused {
    get {
      local
      GuiControlGet, v, % this.GuiHwnd ":Focus"
      return (v=this.ClassNN)
    }
  }

  Value {
    get {
      local
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        this.Opt("+AltSubmit")
      GuiControlGet, v,, % this.Hwnd
      return v
    }
    set {
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        GuiControl, Choose, % this.Hwnd, % value
      else
        GuiControl,, % this.Hwnd, % value
      return value
    }
  }

  Text {
    get {
      local
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        this.Opt("-AltSubmit")
      GuiControlGet, v,, % this.Hwnd
      return v
    }
    set {
      if (this.Type~="i)^(ListBox|DDL|ComboBox|Tab)$")
        GuiControl, ChooseString, % this.Hwnd, % value
      else
        GuiControl,, % this.Hwnd, % value
      return value
    }
  }

}
}    ;==> Class End


			;================= The End =================

			;




给TA捐赠
共{{data.count}}人
人已捐赠
其他应用教程案例

根据银灿IS903的CD-ROM盘符打开光盘文件索引数据库Index-Your-Files-2024年8月15日

2024-8-15 16:57:31

其他应用教程案例

每隔10分钟定时关闭并重启夸克网盘电脑客户端,防止下载器卡死宕机死机停止下载的AutoHotkey脚本2024年8月14日

2024-8-24 21:48:30

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
有新私信 私信列表
搜索