ahk1开发的爬取搜狗网页并获取结果的ocr工具,同时具备翻译功能,可以很好的对不同语言的识别并翻译,包括汉语,日语,英语,汉语,繁体等进行识别和翻译,总之参考搜狗网页介绍。

1、背景

由于在写搜狗翻译本地化的时候无意看到还有ocr,就顺带一起嫖过来了、

搜狗:年轻人耗子尾汁…

2、实现原理

调试搜狗所有网页发现必需获取特定的cookie和查看js代码中的部分算法才能上传图片,先请求三个地址获取cookie然后用ahk实现js的算法补全参数,调用上传地址上传图片获取返回的json ,然后解析json渲染到activeX控件中。

  1. 截图实现:使用snipast 截图 (优点截图功能强大)
  2. 上传实现:调用ctrl+c (snipast截图快捷键)把图片存到粘贴板,通过GDI+把图片处理后存为png文件有些过小的图片需要缩放
  3. post请求:ADODB.Stream来实现post的二进制上传
  4. 解析json:利用js的eval函数
  5. 显示:gui的activeX显示html文件,依旧使用扣取html的方法,掏空body把结果组成的标签放进去,剩下的就交给搜狗网页的兼容了。

优点:

  • 不需依赖外部ahk文件或第三方文件,和注册三方云接口来实现。
  • 可以识别很多语言包括中日韩英等,同时除了中文外的其它语言还可以得到翻译后的中文。
  • 界面美观简洁,翻译结果准确率高。

缺点:

  • 需要联网才能实现这些功能
  • 由于高度依赖搜狗网站,如果搜狗网站结构发生或者代码发生变化可能导致无法使用。
  • 第一次使用时要访问3个不必要网址速度慢,后面会缓存结果,只访问一个网址即可得到结果速度会提升。

3、使用方法

  1.  必须安装snipast 并且具备快捷键ctrl+c保存图(默认就是)
  2. 在snipast选中区域时按ctrl+1快捷键就可以识别图片(快捷键可自己在代码中修改,最好不要和snipast的冲突)

tip:其实自己也可以用gdi+实现屏幕截图,后来详想想没啥必要。一是比较麻烦代码量会增大,二是snipast确实很强大用了就不想用其它截图了。

#Persistent
#SingleInstance
#InstallMouseHook
#InstallKeybdHook
#KeyHistory 499

; 强制使用32位运行
if !(A_IsUnicode=1 and A_PtrSize=4) {
  SplitPath, A_AhkPath, , dir
  Run, %dir%\AutoHotkeyU32.exe "%A_ScriptFullPath%"
  ExitApp
}

CoordMode,Mouse  ;全局获取模式
OnMessage(0x100 , "keyboard_message_callback") ;键盘事件
Return


; F1键调用搜狗OCR剪贴板的图片内容
F1::SoGouOcr.show()

;只对Snipast生效
#IfWinActive  Snipper - Snipaste
$^1::
     SoGouOcr.show()
     return ;
#if

;移动选框
WM_LBUTTONDOWN(){
    Static init:=OnMessage(0x0201, "WM_LBUTTONDOWN")
    if(A_Cursor="Arrow")
        PostMessage, 0xA1, 2
    return ;
}

 ;键盘事件的回调函数
keyboard_message_callback(wparam,lparma,msg)
{
   WinGetActiveTitle, active_title
;   tooltip ,% active_title ":"  wParam
   if((wparam=13 || wparam=27)) ;响应回车事件和esc事件
   {
       Gui sgocr:destroy
   }
}

;复制原文
srcCopyButton1_OnClick()
{
    ; msgBox % SoGouOcr.result_source " :" SoGouOcr.result_trans
    clipboard:=SoGouOcr.result_source
    Gui sgocr:destroy
}

;复制翻译后
srcCopyButton2_OnClick()
{
    ; msgBox % SoGouOcr.result_source " :" SoGouOcr.result_trans
    clipboard:=SoGouOcr.result_trans
    Gui sgocr:destroy
}

;关闭窗口事件
exitBtn1_OnClick()
{
;    msgBox click
    Gui sgocr:destroy
}

;ico点击动画
label_ico_click_animate:
{
    SoGouTrans.animate_timer_count:=SoGouTrans.animate_timer_count+1
    if(SoGouTrans.html_ready || SoGouTrans.ico_new|| SoGouTrans.animate_timer_count*SoGouTrans.animate_timer_gap>SoGouTrans.animate_out_time)
    {
        setTimer ,label_ico_click_animate,off ; 关闭定时器
        GuiControl,translate:hide ,Ico_wait_text1 ;停止显示动画
        GuiControl,translate:hide ,Ico_wait_text2 ;停止显示动画
        GuiControl,translate:hide ,Ico_wait_text3 ;停止显示动画
        return
    }
    ico_wait_shape :=Util.decodeUtf8(SoGouTrans.ico_wait_shape)
    ico_wait_shape :=SoGouTrans.ico_wait_shape
    ico_size:=SoGouTrans.ico_size
    wait1_offset_x:=1.5*ico_size
    wait2_offset_x:=2*ico_size
    wait3_offset_x:=2.5*ico_size
    ico_color:=SoGouTrans.ico_color
    i:=SoGouTrans.animate_timer_count ;INDEX 从1开始
    if(i=1)
    {
        global Ico_wait_text1
        global Ico_wait_text2
        global Ico_wait_text3
    }
    if(i<=3)
    {
        offset_x:=(0.46*i+1)*ico_size-SoGouTrans.point_to_ico
        Gui ,translate:Add, Text, vIco_wait_text%i% x%offset_x% y0 c%ico_color%, % ico_wait_shape  ; label_gui_event标签处理gui事件
    }else
    {
        loop ,% mod(i,4) ;显示
        {
;            tooltip loop1=Ico_wait_text%A_index%
            GuiControl,translate:show ,Ico_wait_text%A_index%
        }
        loop ,% 3-(mod(i,4)) ;隐藏
        {
            hide_index:=4-A_index
            GuiControl,translate:hide ,Ico_wait_text%hide_index%
        }
    }
;    MouseGetPos ,x ,y
;    toolTip % "x:" x " y:" y
    return
}

;延时后去掉ico
label_destroy_ico:
{
    SoGouTrans.ico_timer_count:=SoGouTrans.ico_timer_count+1
    IfWinNotExist ,% SoGouTrans.html_title
    {
            if(SoGouTrans.ico_timer_count*50>=SoGouTrans.ico_destroy_time)
            {
                 Gui, Translate:destroy
                 setTimer ,label_destroy_ico,off
                 setTimer, label_ico_click_animate ,off
            }
    }
    return ;
}

;gui事件处理 https://ahkcn.sourceforge.net/docs/Variables.htm#GuiEvent
label_gui_event:
{
    if(A_GuiControl="Ico_text" && A_GuiControlEvent="Normal") ;点击翻译图标时触发
    {
            ;初始化并渲染翻译结果GUI
         try SoGouTrans.init_transhtml_gui(1)
;        msgBox ok
    }
    return ;
}

;搜狗图片文字识别
class SoGouOcr
{
    static url1:="https://fanyi.sogou.com/" ;搜狗主页 ,获取带有SNUID相关cookie
    static url2:="https://fanyi.sogou.com/picture" ;搜狗图片识别网页 ,获取uuid
    static url3:="https://pb.sogou.com/cl.gif?" ;打开文件会发送一个get请求 ,获取带有SNUID相关cookie
    static url4:="https://fanyi.sogou.com/api/transpc/picture/upload" ;上传文件接口,需要携带 snuid和FQV相关cookie
    static boundary = "----WebKitFormBoundaryaEHpMn3lywBtjPfE" ;formData边界
    static User_Agent:="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
    static cookie:="" ;访问url产生的cookie
    static uuid:="" ;方位url2返回的请求头中的uuid
    static snipaste_title:="Snipper - Snipaste"
    static html_header:="" ;<body>之前
    static html_footer:="" ;</body>之后
    static html_zoom:=0.7 ;网页缩放
    static font_size:=25 ;设置字体大小
    static html_top_color:="00ff00" ;顶部边框颜色
    static pic_min_w:=40 ;图片最小允许宽度,小于这高度就会按照pic_scale放大
    static pic_min_h:=30 ;图片最小允许高度,小于这高度就会按照pic_scale放大
    static pic_scale:=2.5 ; 图片缩放比例
    static result_source:="" ;返回的ocr识别
    static result_trans:="" ;返回的ocr识别的翻译
    static gui_w:=500 ;没有缩放之前的gui大小
    ;请求所有的url,
    request_sogou_url(filepath)
    {
        req := ComObjCreate("WinHttp.WinHttpRequest.5.1")
        if(!SoGouOcr.cookie)
            SoGouOcr.get_url123(req)
        ObjRelease(req) ;释放内存
        return SoGouOcr.get_url4(req,filepath)
    }
    ;打开搜狗翻译主页主要为了获取cookie中的snuid(url3,url4中),FQV(url中) ,wuid(url3中)使用
    get_url123(req)
    {
        ;①请求url1
;        req.SetProxy(2, "127.0.0.1:8888") ;设置fiddler抓包服务器
        req.Open("get", SoGouOcr.url1)
        req.setRequestHeader("User-Agent",SoGouOcr.User_Agent) ;在open之后
        req.send()
;        req.WaitForResponse()
;        fileAppend  ,%   req.GetAllResponseHeaders(),C:\Users\Administrator\Desktop\cookie.txt
        headers:=req.GetAllResponseHeaders() ;获取所有相应头
;        msgBox % headers "`r`n" Util.getCookieMap(headers)
        SoGouOcr.cookie:=Util.getCookieMap(headers)
        ;②请求url2
        req.Open("get", SoGouOcr.url2)
        req.send()
;        req.WaitForResponse()
        SoGouOcr.uuid:=req.GetResponseHeader("UUID:")  ;获取相关cookie
        ;③请求url3
        Random, rand, 0.0, 1.0
        _t:=Util.getTimeStamp()
        _r:=floor(1000* rand)
        uuid:=SoGouOcr.uuid
        snuid:=SoGouOcr.cookie.snuid.value
        wuid:=SoGouOcr.cookie.wuid.value
        url3:=SoGouOcr.url3
        url3_with_param = %url3%uigs_productid=vs_web&vstype=translate&snuid=%snuid%&pagetype=index&type=imgtrans&uuid=%uuid%&fr=default&terminal=web&onerror=true&wuid=%wuid%&overIe10=1&abtest=3&uigs_cl=upload_click_home&_t=%_t%&_r=%_r%&uigs_st=0
;        msgBox % url3_with_param
        req.Open("get", url3_with_param)
        req.send()
;        req.WaitForResponse()
    }

    ;④.上传文件
    get_url4(req,filepath)
    {
        current_cookie:=% "ABTEST="SoGouOcr.cookie.ABTEST.value ";IPLOC="SoGouOcr.cookie.IPLOC.value ";SNUID=" SoGouOcr.cookie.SNUID.value ";FQV=" SoGouOcr.cookie.FQV.value
;        current_cookie=ABTEST=6|1673104302|v17; IPLOC=CN5101;SNUID=DD5759A6D2D620B6A66D186BD31414E5;FQV=c7862eb243f3dfbcf3b287dcc047f0e7
;        src:="C:\Users\Administrator\Desktop\xx1.png"
;        src:="C:\Users\Administrator\Desktop\xx2.png"
        while(!FileExist(filepath)) ;延迟3s中就退出
        {
            sleep ,50
            if(A_index>=60)
                return
        }
        src:=filepath
        extra_data={"from":"auto","to":"zh-CHS","imageName":"xx.png"}
        sec_ch_ua= "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"
        objParam := {"fileData":[src],"fuuid":SoGouOcr.uuid,"extraData":extra_data} ;post数据
        Util.getPostFormBinData(PostData, hdr_ContentType, objParam)
        req.Open("POST", SoGouOcr.url4,true)
        req.SetRequestHeader("Content-Type", hdr_ContentType)
        req.SetRequestHeader("sec-ch-ua",sec_ch_ua)
        req.SetRequestHeader("sec-ch-ua-mobile","?0")
        req.SetRequestHeader("sec-ch-ua-platform","Windows")
        req.SetRequestHeader("sec-Fetch-Dest","empty")
        req.SetRequestHeader("sec-Fetch-Mode","cors")
        req.SetRequestHeader("sec-Fetch-Site","same-origin")
        req.SetRequestHeader("User-Agent",SoGouOcr.User_Agent)
        req.SetRequestHeader("cookie",current_cookie)
        req.Send(PostData)
        req.WaitForResponse()
        result:=req.ResponseText
;        fileAppend ,% result ,C:\Users\Administrator\Desktop\xx2.json
        return result
    }
    ;获取关键数据,成功返回map {contents:"xxx" content_list:[xx1,xx2,xx3]} 主要针对多行数据
    get_ocr_resultMap(filepath)
    {
        result:=SoGouOcr.request_sogou_url(filepath)
;        msgBox % result
;        fileAppend  % result,ocr.json
        rJson:=Util.parseJsonStr(result)
        retMap :={}
        if(rJson.status=0)
        {
            data:=rJson.data
            try{
                data["result"]
            }catch e{
                return
             }
            content_results:=data["result"]
;            msgBox % content_results[0]
            contents:=""
            trans:=""
            content_list:=[]
            trans_list:=[]
            for content in content_results
            {
                 contents:=contents content.content "`r`n"
                 content_list[A_index]:=content.content
                 trans:=trans content.trans_content "`r`n"
                 trans_list[A_index]:=content.trans_content
            }
            retMap["contents"]:=RTrim(contents,"`r`n")
            retMap["content_list"]:=content_list
            retMap["trans"]:=RTrim(trans,"`r`n")
            retMap["trans_list"]:=trans_list
;            msgBox % retMap.contents
            return retMap
        }
    }

    ;显示翻译结果
    show(flag="raw")
    {
;         msgBox show
         clipboard_save:=ClipboardAll
         SetKeyDelay, 0
         ControlSend, , ^c, % SoGouOcr.snipaste_title ;保存图片
         clipWait,3,1 ;第一个参数是等待的s数,第二个参数1是等待任意类型,0(缺省) 等待文本文件等、
         MouseGetPos ,xpos,ypos
         SoGouTrans.create_ico_gui(xpos,ypos)
         WinSet, AlwaysOnTop, ON, % SoGouTrans.ico_title
         SoGouTrans.html_ready:=0 ;加载完成变成1
         SoGouTrans.ico_new:=0 ;创建新的ico变成1
         SoGouTrans.animate_timer_count:=0 ;执行一次加一次
         SetTimer,label_ico_click_animate,% SoGouTrans.animate_timer_gap ;timer后面必须是一个timer不然无法异步执行
         filepath:=SoGouOcr.save_pic()
;         msgBox % filepath
;         fileAppend ,% "`n" filepath ,%A_Temp%\ahkFYtmp.txt
         if(!filepath) ;处理图片
         {
            clipboard:=clipboard_save ;恢复粘贴板
            return
         }
         clipboard:=clipboard_save ;恢复粘贴板
;         fileappend ,% filepath ,%A_Temp%\ahkFYtmp.txt
         SoGouOcr.result_trans:=""
         SoGouOcr.result_source:=""
         content_map:=SoGouOcr.get_ocr_resultMap(filepath) ;图片渲染的数据
         SoGouOcr.result_source:=content_map.contents
         SoGouOcr.result_trans:=content_map.trans  ; 翻译结果
         if(content_map)
            SoGouOcr.create_ocr_html(content_map,flag) ;创建gui
         IfExist ,% filepath ;清除tmp中图片
            FileDelete, % filepath
         SoGouTrans.html_ready:=1
         Gui ,translate:destroy
    }

    ;创建html的GUI
    create_ocr_html(content_map,flag)
    {
        MouseGetPos ,xpos,ypos
        xpos:=xpos-20
        ypos:=ypos-20
        result_html:=SoGouOcr.get_result_guihtml(content_map,flag)
        global WB2 ;浏览器对象
        global MenuHwnd2 ;句柄
        global srcCopyButton1 ;复制按钮1
        global srcCopyButton2 ;复制按钮2
        global exitBtn1 ;退出按钮
;        global convertBtn1 ;切换按钮
        gui_width:=SoGouOcr.gui_w * SoGouOcr.html_zoom
        html_title:="ahk_sogouocr_result_v1"
        Gui ,sgocr:New,,% html_title
;        sleep ,100
        Gui ,sgocr:Add, ActiveX,x0 y0 w%gui_width% h1080 vWB2, Shell.Explorer  ; 最后一个参数是ActiveX组件的名称。
        Gui, sgocr:Color, 30f0ca
;        Gui ,sgocr: -Caption +ToolWindow +HwndocrHwnd
        Gui ,sgocr:+LastFound  +AlwaysOnTop -Caption +ToolWindow +HwndocrHwnd
        WB2.silent := true ;Surpress JS Error boxes
        Util.Display(WB2,result_html)
        while WB2.readystate != 4 or WB2.busy
                sleep 10
        div_h:=WB2.document.getElementById("mainDiv").offsetHeight ;当前div高度
        srcCopyButton1 := WB2.document.getElementById("src-clipboard") ;复制按钮
        srcCopyButton2 := WB2.document.getElementById("target-clipboard") ;复制按钮
        exitBtn1 := WB2.document.getElementById("exitBtn1") ;退出
        ComObjConnect(srcCopyButton1, "srcCopyButton1_")
        ComObjConnect(srcCopyButton2, "srcCopyButton2_")
        ComObjConnect(exitBtn1, "exitBtn1_")
;        ComObjConnect(convertBtn1, "convertBtn1_")
        div_h:=div_h*SoGouOcr.html_zoom
        ;边界检测
        xpos:= xpos+gui_width>A_ScreenWidth?A_ScreenWidth-gui_width:xpos
        ypos:= ypos+div_h>A_ScreenHeight-40?A_ScreenHeight-div_h-40:ypos
        Gui sgocr:Show  ,x%xpos% y%ypos% w%gui_width% h%div_h%
        Util.FrameShadow(ocrHwnd)
;        msgBox ok
    }

    ;获取渲染html ,清空body然后放入构造的html
    get_result_guihtml(content_map,flag)
    {
        url2:="https://fanyi.sogou.com/picture" ;搜狗图片识别网页 ,获取uuid
        WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1") ;创建http对象
        WebRequest.Open("GET",url2)  ;必须有http://
        WebRequest.Send()
        result := WebRequest.ResponseText
        ObjRelease(WebRequest) ;释放内存
        start_element=<!--[if lte IE 9]> <script> ;搜索结果只有一个
        end_element=</div><script> ; 从开始元素开始搜索
        StringReplace, result, result, "//,"https:// ,All ;把路径转换为绝对路径
        if(!SoGouOcr.head_html||!SoGouOcr.footer_html)
        {
            start_pos:=instr(result,start_element,1,1) ;找到body开头
            if(!start_pos)
                return ;
            end_pos:= instr(result,end_element,1,start_pos) ;参数依次是1.目标字符,2.要匹配的字符,3.是否大小写消息敏感,4.起始位置
            if(!end_pos)
                return
            SoGouOcr.head_html:=subStr(result,1,start_pos-1)
            SoGouOcr.footer_html:=subStr(result,end_pos+strLen("</div>"))  ;substr不指定长度就是直接取到最后一个字符
        }
        zoom:=SoGouOcr.html_zoom
        font_size:=SoGouOcr.font_size
        if(strLen(SoGouOcr.result_source)>15)
            font_size:=font_size*0.9
        if(strLen(SoGouOcr.result_source)>30)
            font_size:=font_size*0.9
        if(strLen(SoGouOcr.result_source)>40)
            font_size:=font_size*0.9
        border_color:=SoGouOcr.html_top_color
        span_elements:="" ;识别后的文
        span_elements2:=""  ;翻译后
        exit_left:=zoom*SoGouOcr.gui_w-22,exit_top:=10,exit_zoom:=0.4
        convert_left:=10, convert_bottom:=30,convert_zoom:=0.5
        gui_w:=SoGouOcr.gui_w * SoGouOcr.html_zoom
        gui_r:=gui_w+10 ;右滑块宽度10px
        to_right=window.scrollTo(%gui_r%,0)
        to_left=window.scrollTo(0,%gui_w%)
;        main_div=<div id="mainDiv" style="zoom:%html_scala%;border-top:5px solid #%border_color%;width:%div_w%">
        main_div=<div id="mainDiv" style="zoom:%zoom%;border-top:5px solid #%border_color%">
        div_element_start=%main_div%<div class="trans-box pic"><div class="pic-result-box"><div class="pic-from"><div class="text-box" style="font-size:%font_size%px" ><p>
        html_style=<style>.source_trans_convert{width: 30px;height: 30px;z-index:100;zoom:%convert_zoom%;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -372px -176px;}.source_trans_convert:hover{	cursor:pointer;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;	background-position: -372px -142px;zoom:0.51}.html_gui_exit{width: 38px;height: 38px;zoom:%exit_zoom%;position: fixed;left:%exit_left%px;top: %exit_top%px;z-index:2;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -78px -319px;}.html_gui_exit:hover{cursor:pointer;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -38px -319px;zoom:0.402}</style>
        html_script=<script>var flag=true;window.onload = function(){var div_h = document.getElementById('mainDiv').offsetHeight;document.getElementById('convertBtn1').style.position="fixed";document.getElementById('convertBtn1').style.top= (div_h*%zoom%-%convert_bottom%)+"px";document.getElementById('convertBtn1').style.left="%convert_left%px";};function convert_click(){if(flag){%to_right%}else{%to_left%}flag=!flag}</script>
        ico_div=<div class="html_gui_exit" id="exitBtn1"></div><div class="source_trans_convert"  onclick="convert_click()" id="convertBtn1"></div>
        ;渲染的数据是<span id="left-17-s">SMS202301170423250412</span>
        arr1:= content_map.content_list
        arr2:=content_map.trans_list
;        msgBox % "content:" content_map.contents
        for k,v in arr1
           span_elements=%span_elements% <span id="left-%A_index%-s">%v%</span>
        for  m,n in arr2
           span_elements2=%span_elements2% <span id="right-%A_index%-s">%n%</span>
;        msgBox % "html_frag:" span_elements
;        msgBox % "html_frag:" span_elements2
        copy:=Util.decodeUtf8("\u590D\u5236") ;复制
        right_copy=<div id="target-clipboard" class="btn-copy" >%copy%</div>
        right_element=<div class="pic-to"><div class="text-box"><p>%span_elements2%</p></div>%right_copy%</div>
        div_element_end=</p></div><div id="src-clipboard" class="btn-copy">%copy%</div></div>%right_element%</div></div>
        result_html:=SoGouOcr.head_html html_style html_script ico_div div_element_start span_elements div_element_end SoGouOcr.footer_html
        fileAppend  , % result_html ,C:\Users\Administrator\Desktop\result.html ,utf-8
        return result_html
    }

    ;保存图片到临时文件夹并返回文件
    save_pic(filename:="")
    {
        imageUtil.gdiplusStartup() ;开始GDI
        pBitmap:=imageUtil.from_clipboard()
;        msgBox % "pBitmap:" pBitmap
        if(pBitmap<0) ;获取粘贴板数据
            return ;
        DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0) ;获取图片宽度
        DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0) ;获取图片高度
;        msgBox % "width:height=" width ":" height
        if(width<SoGouOcr.pic_min_w || height<SoGouOcr.pic_min_h )
        {
            imageUtil.BitmapScale(pBitmap,SoGouOcr.pic_scale) ;缩放
        }
;        msgBox % "pBitmap3:" pBitmap
        if(!filename)
        {
            filename:=A_temp "\ahk_ocr_" A_YYYY "-" A_MM "-" A_DD "-" A_Hour "-" A_Min "-" A_Sec ".png"
            filename:=imageUtil.put_file(pBitmap,filename)
        }
        imageUtil.gdiplusShutdown() ;关闭GDI
        return filename
    }
}
; ==================== Util ====================
Class Util
{
       ;字符串转换为二进制
       BinArr_FromString(str) {
           oADO := ComObjCreate("ADODB.Stream")
           oADO.Type := 2 ; adTypeText
           oADO.Mode := 3 ; adModeReadWrite
           oADO.Open
           oADO.Charset := "UTF-8"
           oADO.WriteText(str)
           oADO.Position := 0
           oADO.Type := 1 ; adTypeBinary
           oADO.Position := 3 ; Skip UTF-8 BOM
           return oADO.Read, oADO.Close
       }

       ;把文件转换为二进制
       BinArr_FromFile(FileName) {
           oADO := ComObjCreate("ADODB.Stream")
           oADO.Type := 1 ; adTypeBinary
           oADO.Open
           oADO.LoadFromFile(FileName)
           return oADO.Read, oADO.Close
       }
       ;合并二进制数据
       BinArr_Join(Arrays*) {
           oADO := ComObjCreate("ADODB.Stream")
           oADO.Type := 1 ; adTypeBinary
           oADO.Mode := 3 ; adModeReadWrite
           oADO.Open
           For i, arr in Arrays
               oADO.Write(arr)
           oADO.Position := 0
           return oADO.Read, oADO.Close
       }

       ;把二进制转换为字符串
       BinArr_ToString(BinArr, Encoding := "UTF-8") {
           oADO := ComObjCreate("ADODB.Stream")
           oADO.Type := 1 ; adTypeBinary
           oADO.Mode := 3 ; adModeReadWrite
           oADO.Open
           oADO.Write(BinArr)
           oADO.Position := 0
           oADO.Type := 2 ; adTypeText
           oADO.Charset  := Encoding
           return oADO.ReadText, oADO.Close
       }

       ;把二进制转换为文件
       BinArr_ToFile(BinArr, FileName) {
           oADO := ComObjCreate("ADODB.Stream")
           oADO.Type := 1 ; adTypeBinary
           oADO.Open
           oADO.Write(BinArr)
           oADO.SaveToFile(FileName, 2)
           oADO.Close
       }

      ;产生随机边界
      RandomBoundary() {
          str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
          Sort, str, D| Random
          str := StrReplace(str, "|")
          Return SubStr(str, 1, 12)
      }

      ;判断当前类型
      MimeType(FileName) {
          n := FileOpen(FileName, "r").ReadUInt()
          Return (n        = 0x474E5089) ? "image/png"
               : (n        = 0x38464947) ? "image/gif"
               : (n&0xFFFF = 0x4D42    ) ? "image/bmp"
               : (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
               : (n&0xFFFF = 0x4949    ) ? "image/tiff"
               : (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
               : "application/octet-stream"
      }

      ;解析json字符串、
      parseJsonStr(jsonStr)
      {
           if(inStr(jsonStr,"`\")) ;包含转义字符
           {
               tmp_str=EABD9414432D47859B9BCD78ACA11848
               StringReplace, jsonStr, jsonStr, `\ ,% tmp_str,replaceAll
               StringReplace, jsonStr, jsonStr, `" ,`\`",replaceAll
               StringReplace, jsonStr, jsonStr, % tmp_str ,`\`\,replaceAll
           }else{
               StringReplace, jsonStr, jsonStr, `" ,`\`",replaceAll
           }
           oSC := ComObjCreate("ScriptControl")
           oSC.Language := "JScript"
           Script = var Encoded = eval("(%jsonStr%)")
           oSC.ExecuteStatement(Script)
           result:=oSC.Eval("Encoded")
           ObjRelease(oSC)
           Return result
      }

     ;返回一个cookie 的map={a:{value:"hello",path:"/" , expires:"Sun, 22 Jan 2023 03:46:59 GMT"},cookie:"xxx",size:n}
     ;req.GetAllResponseHeaders()  获取所有cookie
     getCookieMap(headers)
     {
         cookie_map:={}
         cookie_str:=""
;          msgBox % headers
         size:=0
         Loop, parse, headers, `n, `r  ; 在 `r 之前指定 `n, 这样可以同时支持对 Windows 和 Unix 文件的解析.
         {
             head_line:=trim(A_loopField)
;              msgBox % head_line
             tmp_map:={}
             key:=""
             if(inStr(head_line ,"Set-Cookie:")=1)
             {
                 size:=size+1
                 cookie_str:=cookie_str  head_line "`r`n"
                 StringReplace ,cookie_value,% head_line,Set-Cookie:
                 key_value_arr:=StrSplit(cookie_value ,"`;")
                 Loop ,% key_value_arr.MaxIndex()
                 {
                     part_cookie:=key_value_arr[A_index]
                     tmp_arr:=StrSplit(trim(part_cookie) ,"=")
                     if(tmp_arr.MaxIndex()>1)
                     {
                         if(A_index=1)
                         {
                             key := trim(tmp_arr[1])
                             tmp_map["value"]:=trim(tmp_arr[2])
                         }else{
                             tmp_map[trim(tmp_arr[1])]:=trim(tmp_arr[2])
                         }
                      }
                 }
                 cookie_map[key]:=tmp_map
             }
         }
         cookie_map["cookie"]:=cookie_str
         cookie_map["size"]:=size
         return cookie_map
     }

     ;获取post请求中payload ,在post慢中对应数据类型为body中的form-data ,数据类型为二进制
     ;retData 返回的二进制数据,retHeader请求头,objParam入参对象 [] 中会被认为是文件
     ;示例:objParam := {"fileData":[src],"fuuid":"aa409350-e00c-49df-9f20-517796457e68","extraData":extra_data}
     ;
     getPostFormBinData(ByRef retData, ByRef retHeader, objParam) {
         CRLF := "`r`n"
         ; Create a random Boundary
         BoundaryLine := "------------------------------" . Boundary
         ; Loop input paramters
         binArrs := []
         For k, v in objParam
         {
             If IsObject(v) {
                 For i, FileName in v
                 {
                      ;当前二进制数据来源于文件
                     str := BoundaryLine . CRLF
                          . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF
                          . "Content-Type: " . Util.MimeType(FileName) . CRLF . CRLF
                     binArrs.Push( Util.BinArr_FromString(str) )
                     binArrs.Push( Util.BinArr_FromFile(FileName) )
                     binArrs.Push( Util.BinArr_FromString(CRLF) )
                 }
             } Else {
                 str := BoundaryLine . CRLF
                      . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF
                      . v . CRLF
                 binArrs.Push( Util.BinArr_FromString(str) )
             }
         }
         str := BoundaryLine . "--" . CRLF
         binArrs.Push( Util.BinArr_FromString(str) )
         ; Finish
         retData := Util.BinArr_Join(binArrs*)
         retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
     }

     ;获取当前时间戳
     getTimeStamp()
     {
         startTime := "19700101000000"
         nowTime := A_NowUTC
         EnvSub, nowTime, startTime, seconds
         return % nowTime*1000+A_MSec
     }

     ;对utf-8解码
     decodeUtf8(value)
     {
           i := 0
           while (i := InStr(value, "\",, i+1)) {
             if !(SubStr(value, i+1, 1) == "u")
               this.ParseError("\", text, pos - StrLen(SubStr(value, i+1)))
             uffff := Abs("0x" . SubStr(value, i+2, 4))
             if (A_IsUnicode || uffff < 0x100)
               value := SubStr(value, 1, i-1) . Chr(uffff) . SubStr(value, i+6)
           }
         Return,value
      }

      ;窗口加上阴影
       FrameShadow(HGui) {
           DllCall("dwmapi\DwmIsCompositionEnabled","IntP",_ISENABLED) ; Get if DWM Manager is Enabled
           if !_ISENABLED ; if DWM is not enabled, Make Basic Shadow
               DllCall("SetClassLong","UInt",HGui,"Int",-26,"Int",DllCall("GetClassLong","UInt",HGui,"Int",-26)|0x20000)
           else
               VarSetCapacity(_MARGINS,16)
               , NumPut(0,&_MARGINS,0,"UInt")
               , NumPut(0,&_MARGINS,4,"UInt")
               , NumPut(1,&_MARGINS,8,"UInt")
               , NumPut(0,&_MARGINS,12,"UInt")
               , DllCall("dwmapi\DwmSetWindowAttribute", "Ptr", HGui, "UInt", 2, "Int*", 2, "UInt", 4)
               , DllCall("dwmapi\DwmExtendFrameIntoClientArea", "Ptr", HGui, "Ptr", &_MARGINS)
       }

       ;展示html
       Display(WB,html_str) {
           Count:=0
           while % FileExist(f:=A_Temp "\" A_TickCount A_NowUTC "-tmp" Count ".DELETEME.html")
               Count+=1
           FileAppend,%html_str%,%f%,UTF-8
   ;        f=C:\Users\Administrator\Desktop\xx4.html
           WB.Navigate("file://" . f)
   ;        msgBox % WB.readystate
       }

     ;获取粘贴板中png的流
      get_clipboard_pngstream() {
        ; Open the clipboard with exponential backoff.
        loop
           if DllCall("OpenClipboard", "ptr", A_ScriptHwnd)
              break
           else
              if A_Index < 6
                 Sleep (2**(A_Index-1) * 30)
              else throw Exception("Clipboard could not be opened.")
        png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
        if !DllCall("IsClipboardFormatAvailable", "uint", png)
           pStream:=1
        if !(hData := DllCall("GetClipboardData", "uint", png, "ptr"))
           pStream:=2
        ; Allow the stream to be freed while leaving the hData intact.
        ; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745
        DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", pStream:=0, "uint")
        DllCall("CloseClipboard")
        return pStream
      }

      ToBase(n,b)
      {
          return (n < b ? "" : Util.ToBase(n//b,b)) . ((d:=Mod(n,b)) < 10 ? d : Chr(d+55))
      }
}
; ==================== 图像操作类 ====================
class imageUtil{
    ;获取粘贴板数据返回bitmap的指针 pBitmap ,非图片时报错
    from_clipboard() {
      ; Open the clipboard with exponential backoff.
      loop
         if DllCall("OpenClipboard", "ptr", A_ScriptHwnd)
            break
         else
            if A_Index < 6
               Sleep (2**(A_Index-1) * 30)
            else throw Exception("Clipboard could not be opened.")
      ; Fallback to CF_BITMAP. This format does not support transparency even with put_hBitmap().
      if !DllCall("IsClipboardFormatAvailable", "uint", 2)
      {
        DllCall("CloseClipboard")
         return -1
       }
      if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr"))
      {
         DllCall("CloseClipboard")
         return -2
      }
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", pBitmap:=0)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("CloseClipboard")
      return pBitmap
    }
    ;缩放图片传入bitmap的指针,scale缩放 [200,200] 或者1.5倍
    BitmapScale(ByRef pBitmap, scale) {
      if not (IsObject(scale) && ((scale[1] ~= "^\d+$") || (scale[2] ~= "^\d+$")) || (scale ~= "^\d+(\.\d+)?$"))
         throw Exception("Invalid scale.")
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "int*", format:=0)
      if IsObject(scale) {
         safe_w := (scale[1] ~= "^\d+$") ? scale[1] : Round(width / height * scale[2])
         safe_h := (scale[2] ~= "^\d+$") ? scale[2] : Round(height / width * scale[1])
      } else {
         safe_w := Ceil(width * scale)
         safe_h := Ceil(height * scale)
     }
      ; Avoid drawing if no changes detected.
      if (safe_w = width && safe_h = height)
         return pBitmap
      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "ptr*", pGraphics:=0)
      ; Set settings in graphics context.
      DllCall("gdiplus\GdipSetPixelOffsetMode",    "ptr", pGraphics, "int", 2) ; Half pixel offset.
      DllCall("gdiplus\GdipSetCompositingMode",    "ptr", pGraphics, "int", 1) ; Overwrite/SourceCopy.
      DllCall("gdiplus\GdipSetInterpolationMode",  "ptr", pGraphics, "int", 7) ; HighQualityBicubic
      ; Draw Image.
      DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)
      DllCall("gdiplus\GdipSetImageAttributesWrapMode", "ptr", ImageAttr, "int", 3) ; WrapModeTileFlipXY
      DllCall("gdiplus\GdipDrawImageRectRectI"
               ,    "ptr", pGraphics
               ,    "ptr", pBitmap
               ,    "int", 0, "int", 0, "int", safe_w, "int", safe_h ; destination rectangle
               ,    "int", 0, "int", 0, "int",  width, "int", height ; source rectangle
               ,    "int", 2
               ,    "ptr", ImageAttr
               ,    "ptr", 0
               ,    "ptr", 0)
      DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
      ; Clean up the graphics context.
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", pGraphics)
      DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
      return pBitmap := pBitmapScale
    }
    ;把bitmap输出为文件的 bitmap指针(pBitmap) ,filepath:目标位置 比如 c:\xx.png,返回文件位置
    put_file(pBitmap, filepath := "", quality := "") {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517
      extension := "png"
      imageUtil.select_codec(pBitmap, extension, quality, pCodec, ep, ci, v)
      ; Write the file to disk using the specified encoder and encoding parameters with exponential backoff.
      loop
         if !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "ptr", (ep) ? &ep : 0)
            break
         else
            if A_Index < 6
               Sleep (2**(A_Index-1) * 30)
            else throw Exception("Could not save file to disk.")
      return filepath
    }
    select_codec(pBitmap, extension, quality, ByRef pCodec, ByRef ep, ByRef ci, ByRef v) {
        ; Fill a buffer with the available image codec info.
        DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
        DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", &ci := VarSetCapacity(ci, size))
        ; struct ImageCodecInfo - http://www.jose.it-berater.org/gdiplus/reference/structures/imagecodecinfo.htm
        loop {
           if (A_Index > count)
              throw Exception("Could not find a matching encoder for the specified file format.")
           idx := (48+7*A_PtrSize)*(A_Index-1)
        } until InStr(StrGet(NumGet(ci, idx+32+3*A_PtrSize, "ptr"), "UTF-16"), extension) ; FilenameExtension
        ; Get the pointer to the clsid of the matching encoder.
        pCodec := &ci + idx ; ClassID
        ; JPEG default quality is 75. Otherwise set a quality value from [0-100].
        if (quality ~= "^-?\d+$") and ("image/jpeg" = StrGet(NumGet(ci, idx+32+4*A_PtrSize, "ptr"), "UTF-16")) { ; MimeType
           ; Use a separate buffer to store the quality as ValueTypeLong (4).
           VarSetCapacity(v, 4), NumPut(quality, v, "uint")
           ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
           ; enum ValueType - https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.encoderparametervaluetype
           ; clsid Image Encoder Constants - http://www.jose.it-berater.org/gdiplus/reference/constants/gdipimageencoderconstants.htm
           VarSetCapacity(ep, 24+2*A_PtrSize)            ; sizeof(EncoderParameter) = ptr + n*(28, 32)
              NumPut(    1, ep,            0,   "uptr")  ; Count
              DllCall("ole32\CLSIDFromString", "wstr", "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}", "ptr", &ep+A_PtrSize, "uint")
              NumPut(    1, ep, 16+A_PtrSize,   "uint")  ; Number of Values
              NumPut(    4, ep, 20+A_PtrSize,   "uint")  ; Type
              NumPut(   &v, ep, 24+A_PtrSize,    "ptr")  ; Value
        }
     }
   ;开始GDI
   gdiplusStartup()
   {
        DllCall("LoadLibrary", "str", "gdiplus")
        VarSetCapacity(si, A_PtrSize = 4 ? 16:24, 0) ; sizeof(GdiplusStartupInput) = 16, 24
           NumPut(0x1, si, "uint")
        DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", &si, "ptr", 0)
   }
   ;关闭GDI释放内存
   gdiplusShutdown()
   {
       DllCall("gdiplus\GdiplusShutdown", "ptr", pToken)
       DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))
   }
}

; ==================== 搜狗翻译 ====================
class SoGouTrans
{
;     static ico_shape :="H"
;     static ico_shape :="\u2742" ;❂
;     static ico_shape :="\u2664" ;♤
;     static ico_shape :="\u2B1B" ;⬛
;     static ico_shape :="\u2693" ;⚓
;     static ico_shape :="\u2693" ;⚓
;     static ico_shape :="\uD83D\uDC10" ;🐐
;     static ico_shape :="\uD83C\uDF51" ;🍑
;     static ico_shape :="\uD83C\uDFEF" ;🏯
;     static ico_shape :="\u2764" ;❤
;     static ico_shape :="\u265E" ;♞
;     static ico_shape :="\u2663" ;♣
     static ico_shape :="\u265C" ;♜
;     static ico_shape :="\u2602" ;☂
;     static ico_shape :="\u2660" ;♠ ;图形字符对应的unicode编码
;     static ico_wait_shape :="\u25CF" ;● 等待的图标
     static ico_wait_shape :="." ;● 等待的图标
     static ico_size:= 15  ;图标大小, 对象变量必须在new中初始化
     static ico_offset_x:= 10 ;翻译图标偏离鼠标点击位置x轴的距离
     static ico_offset_y:= 6  ;翻译图标偏离鼠标点击位置y轴的距离
     static ico_color:= "00ff00" ;翻译图标颜色,只支持16进制ffffff,不支持颜色单词
     static ico_title:="ahk_translate_ico_v1"  ;翻译的小图标
     static html_title:="ahk_translate_result_v1"  ;翻译的小图标
     static html_scala:=0.8  ;缩放 范围(0-1] (0最小,1最大)
     static ico_hwnd ;小图标的句柄
     static html_hwnd ;html的句柄
     static select_word ;选中字符
     static ico_x  ;图标所在x位置
     static ico_y  ;图标所在y位置
     static hide_flag ;当前图标在单击其它区域后后影藏,除非是
     static trans_doubclick_on:=0 ;是否支持双击选词 0不支持,1支持
     static cursor_drag_gaptime:=300 ;判断鼠标按下到弹起时间差单位ms 来判断拖动
     static cursor_dbclick_gaptime:=100 ;判断鼠标双击时间差单位ms 来判断双击
     static html_head ;缓存头部
     static html_foot ;缓存尾部
     static gui_width:=520 ;显示html的宽度,缩放之前
     static ignore_title ;忽略翻译的
     static grid_width:=15 ;背景取样格子大小,单位像素
     static LB_down_cursor ; 左键按下时鼠标形状
     static timer_flag ; 判断当前是否还在定时器中执行
     static ico_destroy_time :=3000 ;ico在多少ms内不点击的情况下消失
     static ico_timer_count:=0 ;消失ico的timer执行的次数
     static ico_new ;是否新建ico ,1是,0否
     static html_ready :=0 ;当前html页面是否加载完成
     static animate_timer_count:=0 ;当前动画计时器执行次数
     static animate_timer_gap:=200 ;定时器周期时长 单位ms
     static animate_out_time :=10000 ;动画超时时间 (超过时间关闭定时器),单位ms
     static click_down_posx ;
     static cursor_row_min_dist :=6 ;横向距离 单位px
     static use_shortcut_trans :=0 ;是否使用快捷键翻译 ,1使用快捷键翻译,0使用图标翻译。
     static point_to_ico := 5 ;点到ico的距离会减去该值
    ;在鼠标滑动位置创建一个图标
    create_ico_gui(obs_x,obs_y)
    {
        SoGouTrans.ico_x :=obs_x+SoGouTrans.ico_offset_x+3
        SoGouTrans.ico_y :=obs_y+SoGouTrans.ico_offset_y+13
        obs_x:=obs_x+SoGouTrans.ico_offset_x
        obs_y:=obs_y+SoGouTrans.ico_offset_y
        ico_shape :=Util.decodeUtf8(SoGouTrans.ico_shape)
        global Ico_text
        bg_color :=SoGouTrans.calculate_bg_color(obs_x,obs_y,SoGouTrans.ico_size) ;取点分析背景颜色
;        msgBox % bg_color
;        bg_color :=2b2b2b
        ico_size:=SoGouTrans.ico_size
        ico_color:=SoGouTrans.ico_color
        gui_w:=ico_size>16?(2*ico_size-16)+8:ico_size+8
        gui_h:=gui_w
        gui_title:=SoGouTrans.ico_title
        wait3_offset_x:=2.5*ico_size
        gui_w:=wait3_offset_x+10
        Gui ,translate:New, ,% gui_title
        Gui, translate:Color, %bg_color%
;        Gui, translate:Color, ffffff
        Gui ,translate:+LastFound +HwndMenuHwnd +AlwaysOnTop -Caption +ToolWindow
        SoGouTrans.ico_hwnd:=MenuHwnd
        Gui, translate:Font, c%ico_color% q1 thin s%ico_size% ,  Verdana
        Gui ,translate:Add, Text, vIco_text x0 y0 c%ico_color% glabel_gui_event, % ico_shape  ; label_gui_event标签处理gui事件
        WinSet, TransColor, %bg_color% 250 ;设置颜色透明
        Gui ,translate:Show, x%obs_x% y%obs_y% w%gui_w% h%gui_h% NoActivate ;NoActivate 让当前活动窗口继续保持活动状态.
        WinSet, AlwaysOnTop, Off, % gui_title ;去掉总在最上面限制,在切换窗口的时候可以隐藏,但是并不会关闭
        GuiControl,hide ,Ico_wait_text1
        GuiControl,hide ,Ico_wait_text2
        GuiControl,hide ,Ico_wait_text3
        select_str:=Util.getSelectStr() ;获取光标选中字符
        StringReplace, select_str,select_str, `r,, All
        StringReplace, select_str,select_str, `n,, All
        StringReplace, select_str,select_str, `t,, All
        SoGouTrans.select_word:= select_str
        setTimer ,label_destroy_ico,off ;去掉之前的定时器
        setTimer ,label_destroy_ico ,50 ;延时后去掉ico
        SoGouTrans.ico_timer_count:=0
        SoGOuTrans.ico_new:=1
;        fileappend ,% SoGouTrans.select_word,C:\Users\Administrator\Desktop\select.txt
;        msgBox % strlen(trim(SoGouTrans.select_word,OmitChars = "`r`n"))
;        msgBox % strLen(select_str)
        return 1
    }

    ;计算背景颜色,采集20个点,判断颜色最多的,去掉背景会残留只有融入背景才能让图形更圆滑
    calculate_bg_color(obs_x,obs_y,font_size)
    {
        grid_width:=SoGouTrans.grid_width ;取样格子宽度
        x:=obs_x+font_size+10
        y:=obs_y+font_size+10
        color_map:={}
        loop , 5
        {
            i:=A_Index
            loop 4
            {
                j:=A_Index
                PixelGetColor,current_color, % x+j*grid_width,% obs_y+i*grid_width ,RGB
;                msgBox % x+j*grid_width "," y+i*grid_width " color:" current_color
                if(!color_map[current_color])
                    color_map[current_color]:=1
                else
                   color_map[current_color]:=color_map[current_color]+1
            }
        }
        tmp_count:=0
        tmp_color:=0
        for key,value in color_map
        {
           if(color_map[key]>tmp_count)
           {
                tmp_count:=color_map[key]
                tmp_color:=key
           }
        }
;        msgBox % msg "color:" Util.ToBase(tmp_color,16)
        return Util.ToBase(tmp_color,16)
    }
}

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。