底部有v2版BTT

 

这是用 GDI+ 完全重写的 ToolTip 。

BeautifulToolTip ,以下简称 BTT  。    作者:空


为什么要重写一个 ToolTip 呢,因为下面4个问题真的很烦人。

  • 文本难以对齐

  • 闪烁

  • 坐标乱飘

 

  • 难以在显示前预知大小

BTT 被设计为可以完全取代 ToolTip ,所以它完全兼容 ToolTip 的语法及设置。

  • 显示一个内容
btt("This is BeautifulToolTip")​
  • 清空一个内容
btt()​
  • 指定 x,y 及 WhichToolTip
btt("This is BeautifulToolTip`n`nWhichToolTip = 2",500,500,2)
Sleep, 5000
btt(,,,2)
  • CoordMode 命令也对 BTT 生效
CoordMode, ToolTip, Screen
btt("This is BeautifulToolTip")
Sleep, 5000
btt()

 


除了上面基本用法, BTT 还拥有多个内置样式,使用依然简单。

  • 使用内置样式“Style1”
btt("This is BeautifulToolTip",,,, "Style1")

更多内置样式的效果

自定义样式也非常简单,只需更改模板参数!

#SingleInstance Force
SetBatchLines, -1
Text=
(
使用模板可以轻松创建自己的风格。
欢迎分享,带张截图!!!
Use template to easily create your own style.
Please share your custom style and include a screenshot.
It will help a lot of people.
)
; 照着模板改参数就可以创建自己的风格了。建好后可以添加到 btt() 函数里,就可以变内置风格了。
Template :=  {Border:20                                      ; If omitted, 1 will be used. Range 0-20.
            , Rounded:30                                     ; If omitted, 3 will be used. Range 0-30.
            , Margin:30                                      ; If omitted, 5 will be used. Range 0-30.
            , TabStops:[50, 80, 100]                         ; If omitted, [50] will be used. This value must be an array.
            , BorderColor:0xffaabbcc                         ; ARGB
            , BorderColorLinearGradientStart:0xff16a085      ; ARGB
            , BorderColorLinearGradientEnd:0xfff4d03f        ; ARGB
            , BorderColorLinearGradientAngle:45              ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
            , BorderColorLinearGradientMode:1                ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
            , TextColor:0xff112233                           ; ARGB
            , TextColorLinearGradientStart:0xff00416a        ; ARGB
            , TextColorLinearGradientEnd:0xffe4e5e6          ; ARGB
            , TextColorLinearGradientAngle:90                ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
            , TextColorLinearGradientMode:1                  ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
            , BackgroundColor:0xff778899                     ; ARGB
            , BackgroundColorLinearGradientStart:0xff8DA5D3  ; ARGB
            , BackgroundColorLinearGradientEnd:0xffF4CFC9    ; ARGB
            , BackgroundColorLinearGradientAngle:135         ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
            , BackgroundColorLinearGradientMode:1            ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
            , Font:"Font Name"                               ; If omitted, ToolTip's Font will be used. Can specify the font file.
            , FontSize:20                                    ; If omitted, 12 will be used.
            , FontRender:5                                   ; If omitted, 5 will be used. Range 0-5.
            , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"}
; Same as Style7
OwnStyle1 := {Border:20
            , Rounded:30
            , Margin:30
            , BorderColor:0xffaabbcc
            , TextColor:0xff112233
            , BackgroundColorLinearGradientStart:0xffF4CFC9
            , BackgroundColorLinearGradientEnd:0xff8DA5D3
            , BackgroundColorLinearGradientAngle:0
            , BackgroundColorLinearGradientMode:8
            , FontStyle:"BoldItalic Underline"}
btt(Text,,,,OwnStyle1)
Sleep, 10000
ExitApp


通过更改 BTT 的 Options 参数,可以很容易的实现一些特效。

  • 淡入
for k, v in [15,35,55,75,95,115,135,155,175,195,215,235,255]
{
  btt(Text,,,,"Style7",{Transparent:v})
  Sleep, 30
}
  • 淡出
for k, v in [240,220,200,180,160,140,120,100,80,60,40,20,0]
{
  btt(Text,,,,"Style7",{Transparent:v})
  Sleep, 30
}

 

  • 改变“渐变角度”参数实现动画效果

  • 窗口跟随
btt(Text,800-1,600-1,,"Style1",{TargetHWND:target})

 

  • 计算最终显示大小
ret := btt("test",,,,,{JustCalculateSize:1})
MsgBox, % "宽:" ret.w " 高:" ret.h

 


除了以上这些 BTT 还实现了

  • 多显示器支持
  • 多DPI支持

但这里就不再演示了,自己看打包的示例或者源码中的说明。


最后,在即没有原版那些烦人问题,又拥有诸多新特性的同时。

BTT 的性能    ——     比原版快 2-1000 倍!!!!!!

  • 原版 CPU 占用率

  • BTT CPU 占用率

  • 耗时对比


强烈建议你把 BTT库 及 引用库 放 AHK 公用 Lib 目录里。

也就是说,把 BTT.ahk 、 Gdip_All.ahk 、 NonNull.ahk 放下面目录里(没有就手动新建)

C:\我的电脑\Documents\AutoHotkey\Lib

 

这样你在每次使用的时候,就不需要再 #Include BTT.ahk 了。


好了,快抛弃 ToolTip 拥抱 BTT 吧!!!秀出你自定义的style!!!

下载地址:

https://github.com/telppa/BeautifulToolTip/releases


更新日志:

  • 2021.10.03
    改变 Include 方式,降低库冲突的可能性。
  • 2021.09.29
    支持设置 TabStops 。
    除 GDIP 库外所有函数内置到 Class 中,降低库冲突的可能性。
  • 2021.04.30
    修复 Win7 下不能运行的 bug 。(2021.04.20 引起)
  • 2021.04.20
    支持根据显示器 DPI 缩放比例自动缩放,与 ToolTip 特性保持一致。
    支持直接使用未安装的本地字体。
    Options 增加 JustCalculateSize 参数,可在不绘制内容的前提下直接返回尺寸。
  • 2021.03.07
    增加 4 种渐变模式。
  • 2021.03.05
    删除渐变方向参数。
    增加渐变角度参数,支持 360° 渐变。
    增加渐变模式参数,支持 4 种模式。
  • 2021.03.03
    文本色支持渐变。
    增加 Style8 。
    增加3个渐变方向。
  • 2021.03.02
    细边框色支持渐变。
    增加 Style7 。
    增加2个渐变方向。
  • 2021.03.01
    BTT 的总在最上级别现在跟 ToolTip 一样高了。
    解决 BTT 被不明原因置底导致误以为没显示的问题。
    增加 Style6 。
    文字显示更加居中。
  • 2021.02.22
    增加返回值与 Transparent 参数。

 


 

BTT v2转写【2022.06.18】.ahk

#Requires AutoHotkey v2.0

btttxt:="
(
使用模板可以轻松创建自己的风格。
欢迎分享,带张截图!!!

Use template to easily create your own style.
Please share your custom style and include a screenshot.
It will help a lot of people.
)"
;样式1:用于正则工具完成情况提示
OwnzztooltipStyle1 := {Border:1
  , Rounded:2
  , Margin:8
  , BorderColorLinearGradientStart:0xff3881a7   ;0xffb7407c
  , BorderColorLinearGradientEnd:0xff3881a7
  , BorderColorLinearGradientAngle:45
  , BorderColorLinearGradientMode:6
  , FontSize:16
  , TextColor:0xFFFFFFFF    ;0xffd9d9db
  , BackgroundColor:0xFF000000  ;0xff26293a
  , FontStyle:"Regular"}
  
;样式2:用于正则工具控件功能提示
OwnzztooltipStyle2 := {Border:1
      , Rounded:8
      , TextColor:0xfff4f4f4
      , BackgroundColor:0xaa3e3d45
      , FontSize:14}

      Style99 :=  {Border:20                                      ; If omitted, 1 will be used. Range 0-20.
        , Rounded:30                                     ; If omitted, 3 will be used. Range 0-30.
        , Margin:30                                      ; If omitted, 5 will be used. Range 0-30.
        , TabStops:[50, 80, 100]                         ; If omitted, [50] will be used. This value must be an array.
        , BorderColor:0xffaabbcc                         ; ARGB
        , BorderColorLinearGradientStart:0xff16a085      ; ARGB
        , BorderColorLinearGradientEnd:0xfff4d03f        ; ARGB
        , BorderColorLinearGradientAngle:45              ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
        , BorderColorLinearGradientMode:1                ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
        , TextColor:0xff112233                           ; ARGB
        , TextColorLinearGradientStart:0xff00416a        ; ARGB
        , TextColorLinearGradientEnd:0xffe4e5e6          ; ARGB
        , TextColorLinearGradientAngle:90                ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
        , TextColorLinearGradientMode:1                  ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
        , BackgroundColor:0xff778899                     ; ARGB
        , BackgroundColorLinearGradientStart:0xff8DA5D3  ; ARGB
        , BackgroundColorLinearGradientEnd:0xffF4CFC9    ; ARGB
        , BackgroundColorLinearGradientAngle:135         ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
        , BackgroundColorLinearGradientMode:1            ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
        , Font:"Font Name"                               ; If omitted, ToolTip's Font will be used. Can specify the font file path.
        , FontSize:20                                    ; If omitted, 12 will be used.
        , FontRender:5                                   ; If omitted, 5 will be used. Range 0-5.
        , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"}

  btt(btttxt,,,,OwnzztooltipStyle1,{Transparent:180,DistanceBetweenMouseXAndToolTip:-100,DistanceBetweenMouseYAndToolTip:-20})
  SetTimer OwnzztooltipEnd ,-3000
  
OwnzztooltipEnd()
{
   btt()
   ExitApp
}





;================================ BTT v2库 ;================================
;BTT转v2 by城西 2022.6.18 https://github.com/liuyi91   :初步测试支持thqby大佬的 v2 beta4
;感谢大佬thqby的悉心指点 https://github.com/thqby/
;BTT作者v1:https://github.com/telppa/BeautifulToolTip


btt(Text:="", X:="", Y:="", WhichToolTip:="", BulitInStyleOrStyles:="", BulitInOptionOrOptions:="")
{
  static BTT
       , Style99 :=  {Border:20                                      ; If omitted, 1 will be used. Range 0-20.
                    , Rounded:30                                     ; If omitted, 3 will be used. Range 0-30.
                    , Margin:30                                      ; If omitted, 5 will be used. Range 0-30.
                    , TabStops:[50, 80, 100]                         ; If omitted, [50] will be used. This value must be an array.
                    , BorderColor:0xffaabbcc                         ; ARGB
                    , BorderColorLinearGradientStart:0xff16a085      ; ARGB
                    , BorderColorLinearGradientEnd:0xfff4d03f        ; ARGB
                    , BorderColorLinearGradientAngle:45              ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
                    , BorderColorLinearGradientMode:1                ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
                    , TextColor:0xff112233                           ; ARGB
                    , TextColorLinearGradientStart:0xff00416a        ; ARGB
                    , TextColorLinearGradientEnd:0xffe4e5e6          ; ARGB
                    , TextColorLinearGradientAngle:90                ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
                    , TextColorLinearGradientMode:1                  ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
                    , BackgroundColor:0xff778899                     ; ARGB
                    , BackgroundColorLinearGradientStart:0xff8DA5D3  ; ARGB
                    , BackgroundColorLinearGradientEnd:0xffF4CFC9    ; ARGB
                    , BackgroundColorLinearGradientAngle:135         ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U)
                    , BackgroundColorLinearGradientMode:1            ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8.
                    , Font:"Font Name"                               ; If omitted, ToolTip's Font will be used. Can specify the font file path.
                    , FontSize:20                                    ; If omitted, 12 will be used.
                    , FontRender:5                                   ; If omitted, 5 will be used. Range 0-5.
                    , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"}

       , Option99 := {TargetHWND:""                                  ; If omitted, active window will be used.
                    , CoordMode:"Screen Relative Window Client"      ; If omitted, A_CoordModeToolTip will be used.
                    , Transparent:""                                 ; If omitted, 255 will be used.
                    , MouseNeverCoverToolTip:""                      ; If omitted, 1 will be used.
                    , DistanceBetweenMouseXAndToolTip:""             ; If omitted, 16 will be used. This value can be negative.
                    , DistanceBetweenMouseYAndToolTip:""             ; If omitted, 16 will be used. This value can be negative.
                    , JustCalculateSize:""}                          ; Set to 1, no content will be displayed, just calculate size and return.

       , Style1 := {TextColor:0xffeef8f6
                  , BackgroundColor:0xff1b8dff
                  , FontSize:14}

       , Style2 := {Border:1
                  , Rounded:8
                  , TextColor:0xfff4f4f4
                  , BackgroundColor:0xaa3e3d45
                  , FontSize:14}

       , Style3 := {Border:2
                  , Rounded:0
                  , TextColor:0xffF15839
                  , BackgroundColor:0xffFCEDE6
                  , FontSize:14}

       , Style4 := {Border:10
                  , Rounded:20
                  , BorderColor:0xff604a78
                  , TextColor:0xffF3AE00
                  , BackgroundColor:0xff6A537F
                  , FontSize:20
                  , FontStyle:"Bold Italic"}

       , Style5 := {Border:0
                  , Rounded:5
                  , TextColor:0xffeeeeee
                  , BackgroundColorLinearGradientStart:0xff134E5E
                  , BackgroundColorLinearGradientEnd:0xff326f69
                  , BackgroundColorLinearGradientAngle:0
                  , BackgroundColorLinearGradientMode:1}

       , Style6 := {Border:2
                  , Rounded:5
                  , TextColor:0xffCAE682
                  , BackgroundColor:0xff434343
                  , FontSize:14}

       , Style7 := {Border:20
                  , Rounded:30
                  , Margin:30
                  , BorderColor:0xffaabbcc
                  , TextColor:0xff112233
                  , BackgroundColorLinearGradientStart:0xffF4CFC9
                  , BackgroundColorLinearGradientEnd:0xff8DA5D3
                  , BackgroundColorLinearGradientAngle:0
                  , BackgroundColorLinearGradientMode:1
                  , FontStyle:"BoldItalic"}

       , Style8 := {Border:3
                  , Rounded:30
                  , Margin:30
                  , BorderColorLinearGradientStart:0xffb7407c
                  , BorderColorLinearGradientEnd:0xff3881a7
                  , BorderColorLinearGradientAngle:45
                  , BorderColorLinearGradientMode:1
                  , TextColor:0xffd9d9db
                  , BackgroundColor:0xff26293a}
  ; 直接在 static 中初始化 BTT 会报错,所以只能这样写
  if !isset(BTT)
    BTT := BeautifulToolTip()
  return BTT.ToolTip(Text, X, Y, WhichToolTip,BulitInStyleOrStyles,BulitInOptionOrOptions)
                    ; 如果 Style 是一个内置预设的名称,则使用对应内置预设的值,否则使用 Styles 本身的值。 Options 同理。
                    ;, BulitInStyleOrStyles=""   ? BulitInStyleOrStyles   : %BulitInStyleOrStyles%
                    ;, %BulitInOptionOrOptions%="" ? BulitInOptionOrOptions : %BulitInOptionOrOptions%)
}


Class BeautifulToolTip extends Map
{
  static DebugMode:=0
  CaseSense := 'off'
  __New()
  {
    ;=if (!this.pToken)
    if (!this.HasOwnProp("pToken") or !this.pToken)
    {
      this.pToken := Gdip_Startup()
      if (!this.pToken)
      {
        MsgBox("Gdiplus failed to start. Please ensure you have gdiplus on your system", "gdiplus error!", 48)
        ExitApp
      }

      ; 多显示器支持
      this.Monitors := MDMF_Enum()
      ; 获取多显示器各自 DPI 缩放比例
      for hMonitor, v in this.Monitors.Clone()
      {
        if (hMonitor="TotalCount" or hMonitor="Primary")
          continue
        ; https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/e421acb801867edb659a54b7473e6e617f2b267b/libs/g-n/Monitor.ahk
        ; ahk 源码里 A_ScreenDPI 就是只获取了 dpiX ,所以这里保持一致
        osv := StrSplit(A_OSVersion, ".")               ; https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw
        if (osv[1] < 6 || (osv[1] == 6 && osv[2] < 3))  ; WIN_8- 。Win7 必须用这种方式,否则会出错
        {
          ;=hDC  := DllCall("Gdi32.dll\CreateDC", "Str", hMonitor.name, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
          hDC  := DllCall("Gdi32.dll\CreateDC", "Str", v.name, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
          dpiX := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "Int", 88) ; LOGPIXELSX = 88
          DllCall("Gdi32.dll\DeleteDC", "Ptr", hDC)
        }
       else
          DllCall("Shcore.dll\GetDpiForMonitor", "Ptr", hMonitor, "Int", 0, "UIntP", &dpiX:=0, "UIntP", &dpiY:=0, "UInt")   ;原文这个type没设置数字
        this.Monitors[hMonitor].DPIScale := this.NonNull_Ret(dpiX, A_ScreenDPI)/96
      }

      ; 获取整个桌面的分辨率,即使跨显示器
      VirtualWidth := SysGet(78)
      VirtualHeight := SysGet(79)
      this.DIBWidth  := VirtualWidth
      this.DIBHeight := VirtualHeight

      ; 获取 ToolTip 的默认字体
      this.ToolTipFontName := this.Fnt_GetTooltipFontName()
      ; create 20 guis for gdi+
      ; 最多20个 ToolTip ,与原版对应。
      _BTTGUI:=map()
      loop 20
      {
        ; _BTT1(GUI 名称) 与 _hBTT1(GUI 句柄) 都是临时变量,后者被储存了。
        _BTTGUI[A_Index]:=GUI("+E0x80000 -Caption +ToolWindow +LastFound +AlwaysOnTop")
        jjtem :=_BTTGUI[A_Index].Hwnd		;设置鼠标穿透属性,配合透明度,实现鼠标穿透  让tooltip永远在最上端		+城西 2021.11.25
      	_BTTGUI[A_Index].Show("NA")
        WinSetExStyle("+32", "ahk_id " jjtem)			;设置鼠标穿透属性,配合透明度,实现鼠标穿透  让tooltip永远在最上端		+城西 2021.11.25
        this["hBTT" A_Index] := _BTTGUI[A_Index].Hwnd
        , this["hbm" A_Index]  := CreateDIBSection(this.DIBWidth, this.DIBHeight)
        , this["hdc" A_Index]  := CreateCompatibleDC()
        , this["obm" A_Index]  := SelectObject(this["hdc" A_Index], this["hbm" A_Index])
        , this["G" A_Index]    := Gdip_GraphicsFromHDC(this["hdc" A_Index])
        , Gdip_SetSmoothingMode(this["G" A_Index], 4)
        , Gdip_SetPixelOffsetMode(this["G" A_Index], 2)  ; 此参数是画出完美圆角矩形的关键
      }
    }
    else
      return
  }

  ; new 后得到的变量在销毁后会自动跳这里来运行,因此很适合做自动资源回收。
  __Delete()
  {
    loop 20
    {   
      /*      ;不知道为什么会出现不能读写能存,改用下面的方式
        Gdip_DeleteGraphics(this["G" A_Index])
      ,  SelectObject(this["hdc" A_Index], this["obm" A_Index])
      , DeleteObject(this["hbm" A_Index])
      , DeleteDC(this["hdc" A_Index])
      */
        this["G" A_Index]:=""
      , this["hdc" A_Index]:=""
      , this["obm" A_Index]:=""
      , this["hbm" A_Index]:=""
      , this["hdc" A_Index]:=""
    }
    ;Gdip_Shutdown(this.pToken)
    this.pToken:=""
  }

  ; 参数默认全部为空只是为了让清空 ToolTip 的语法简洁而已。
  ToolTip(Text:="", X:="", Y:="", WhichToolTip:="", Styles:="", Options:="")
  {
    ; 给出 WhichToolTip 的默认值1,并限制 WhichToolTip 的范围为 1-20
    this.NonNull(&WhichToolTip, 1, 1, 20)
    ; 检查并解析 Styles 与 Options 。无论不传参、部分传参、完全传参,此函数均能正确返回所需参数。
    if (text !="")
      O:=this._CheckStylesAndOptions(Styles, Options)
    Else
    {
      O:={}
      O.Checksum:=""
    }

    ; 判断显示内容是否发生变化。由于前面给了 Options 一个默认值,所以首次进来时下面的 O.Options!=SavedOptions 是必然成立的。
    FirstCallOrNeedToUpdate:=(Text       != (this.HasOwnProp("SavedText" WhichToolTip) ? this["SavedText" WhichToolTip] : "")
                           or O.Checksum != (this.HasOwnProp("SavedText" WhichToolTip) ? this["SavedText" WhichToolTip] : ""))
    if (Text="")
    {
      ; 清空 ToolTip
      Gdip_GraphicsClear(this["G" WhichToolTip])
      UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip])
      ; 清空变量
        this["SavedText" WhichToolTip]        := ""
      , this["SavedOptions" WhichToolTip]     := ""
      , this["SavedX" WhichToolTip]           := ""
      , this["SavedY" WhichToolTip]           := ""
      , this["SavedW" WhichToolTip]           := ""
      , this["SavedH" WhichToolTip]           := ""
      , this["SavedTargetHWND" WhichToolTip]  := ""
      , this["SavedCoordMode" WhichToolTip]   := ""
      , this["SavedTransparent" WhichToolTip] := ""

      return
    }
    else if (FirstCallOrNeedToUpdate)  ; First Call or NeedToUpdate
    {
      ; 加速
      ; 获取目标尺寸,用于计算文本大小时加上宽高限制,否则目标为屏幕时,可能计算出超宽超高的大小,导致无法显示。
        TargetSize:=this._CalculateDisplayPosition(&X, &Y, "", "", O, GetTargetSize:=1)
      ; 使得 文字区域+边距+细边框 不会超过目标宽度。
      , MaxTextWidth:=TargetSize.W - O.Margin*2 - O.Border*2
      ; 使得 文字区域+边距+细边框 不会超过目标高度的90%。
      ; 之所以高度限制90%是因为两个原因,1是留出一些上下的空白,避免占满全屏,鼠标点不了其它地方,难以退出。
      ; 2是在计算文字区域时,即使已经给出了宽高度限制,且因为自动换行的原因,宽度的返回值通常在范围内,但高度的返回值偶尔还是会超过1行,所以提前留个余量。
      , MaxTextHeight:=(TargetSize.H*90)//100 - O.Margin*2 - O.Border*2
      ; 为 _TextToGraphics 计算区域提供高宽限制。
      , O.Width:=MaxTextWidth, O.Height:=MaxTextHeight
      ; 计算文字显示区域 TextArea = x|y|width|height|chars|lines
      , TextArea:=StrSplit(this._TextToGraphics(this["G" WhichToolTip], Text, O, Measure:=1), "|")
      ; 这里务必向上取整。
      ; 向上的原因是,例如 1.2 如果四舍五入为 1,那么最右边的字符可能会显示不全。
      ; 取整的原因是,不取整无法画出完美的圆角矩形。
      ; 当使用 AutoTrim 选项,即自动将超出范围的文字显示为 “...” 时,此时返回的宽高度值是不包含 “...” 的。
      ; 所以加上 “...” 的宽高度后,仍然可能超限。故需要再次检查并限制。
      ; 一旦宽高度超过了限制(CreateDIBSection() 时创建的大小),会导致 UpdateLayeredWindow() 画不出图像来。
      , TextWidth:=Min(Ceil(TextArea[3]), MaxTextWidth)
      , TextHeight:=Min(Ceil(TextArea[4]), MaxTextHeight)

      , RectWidth:=TextWidth+O.Margin*2                                   ; 文本+边距。
      , RectHeight:=TextHeight+O.Margin*2
      , RectWithBorderWidth:=RectWidth+O.Border*2                         ; 文本+边距+细边框。
      , RectWithBorderHeight:=RectHeight+O.Border*2
      ; 圆角超过矩形宽或高的一半时,会画出畸形的圆,所以这里验证并限制一下。
      , R:=(O.Rounded>Min(RectWidth, RectHeight)//2) ? Min(RectWidth, RectHeight)//2 : O.Rounded
      if (O.JustCalculateSize!=1)
      {
        ; 画之前务必清空画布,否则会出现异常。
        Gdip_GraphicsClear(this["G" WhichToolTip])
        ; 准备细边框画刷
        if (O.BCLGA!="" and O.BCLGM and O.BCLGS and O.BCLGE)                    ; 渐变画刷 画细边框
          pBrushBorder := this._CreateLinearGrBrush(O.BCLGA, O.BCLGM, O.BCLGS, O.BCLGE
                                                  , 0, 0, RectWithBorderWidth, RectWithBorderHeight)
        else
          pBrushBorder := Gdip_BrushCreateSolid(O.BorderColor)                  ; 纯色画刷 画细边框

        if (O.Border>0)
          switch R
          {
            ; 圆角为0则使用矩形画。不单独处理,会画出显示异常的图案。
            case "0": Gdip_FillRectangle(this["G" WhichToolTip]                ; 矩形细边框
            , pBrushBorder, 0, 0, RectWithBorderWidth, RectWithBorderHeight)
            Default: Gdip_FillRoundedRectanglePath(this["G" WhichToolTip]     ; 圆角细边框
            , pBrushBorder, 0, 0, RectWithBorderWidth, RectWithBorderHeight, R)
          }
        ; 准备文本框画刷
        if (O.BGCLGA!="" and O.BGCLGM and O.BGCLGS and O.BGCLGE)                ; 渐变画刷 画文本框
          pBrushBackground := this._CreateLinearGrBrush(O.BGCLGA, O.BGCLGM, O.BGCLGS, O.BGCLGE
                                                      , O.Border, O.Border, RectWidth, RectHeight)
        else
          pBrushBackground := Gdip_BrushCreateSolid(O.BackgroundColor)          ; 纯色画刷 画文本框
        switch R
        {
          case "0": Gdip_FillRectangle(this["G" WhichToolTip]                  ; 矩形文本框
          , pBrushBackground, O.Border, O.Border, RectWidth, RectHeight)
          Default: Gdip_FillRoundedRectanglePath(this["G" WhichToolTip]       ; 圆角文本框
          , pBrushBackground, O.Border, O.Border, RectWidth, RectHeight
          , (R>O.Border) ? R-O.Border : R)                                      ; 确保内外圆弧看起来同心
        }
        ; 清理画刷
        Gdip_DeleteBrush(pBrushBorder)
        Gdip_DeleteBrush(pBrushBackground)
        ; 计算居中显示坐标。由于 TextArea 返回的文字范围右边有很多空白,所以这里的居中坐标并不精确。
        O.X:=O.Border+O.Margin, O.Y:=O.Border+O.Margin, O.Width:=TextWidth, O.Height:=TextHeight
        ; 如果显示区域过小,文字无法完全显示,则将待显示文本最后4个字符替换为4个英文省略号,表示显示不完全。
        ; 虽然有 GdipSetStringFormatTrimming 函数可以设置末尾显示省略号,但它偶尔需要增加额外的宽度才能显示出来。
        ; 由于难以判断是否需要增加额外宽度,以及需要增加多少等等问题,所以直接用这种方式自己实现省略号的显示。
        ; 之所以选择替换最后4个字符,是因为一般替换掉最后2个字符,才能确保至少一个省略号显示出来。
        ; 为了应对意外情况,以及让省略号更加明显一点,所以选择替换最后4个。
        ; 原始的 Text 需要用于显示前的比对,所以不能改原始值,必须用 TempText 。
        if (TextArea[5]<StrLen(Text))
          TempText:=TextArea[5]>4 ? SubStr(Text, 1 ,TextArea[5]-4) "…………" : SubStr(Text, 1 ,1) "…………"
        else
          TempText:=Text
        ; 写字到框上。这个函数使用 O 中的 X,Y 去调整文字的位置。
        this._TextToGraphics(this["G" WhichToolTip], TempText, O)
        ; 调试用,可显示计算得到的文字范围。
        /*   ;城西取消
        if (this.DebugMode)
        {
          pBrush := Gdip_BrushCreateSolid(0x20ff0000)
          Gdip_FillRectangle(this["G" WhichToolTip], pBrush, O.Border+O.Margin, O.Border+O.Margin, TextWidth, TextHeight)
          Gdip_DeleteBrush(pBrush)
        }
        */
        ; 返回文本框不超出目标范围(比如屏幕范围)的最佳坐标。
        this._CalculateDisplayPosition(&X, &Y, RectWithBorderWidth, RectWithBorderHeight, O)
        ; 显示
        UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip]
                          , X, Y, RectWithBorderWidth, RectWithBorderHeight, O.Transparent)
        ; 因为 BTT 总会在使用一段时间后,被不明原因的置底,导致显示内容被其它窗口遮挡,以为没有正常显示,所以这里提升Z序到最上面!
        ; 已测试过,此方法效率极高,远超 WinSet, Top 命令。
        ; hWndInsertAfter
        ;   HWND_TOPMOST:=-1
        ; uFlags
        ;   SWP_NOSIZE:=0x0001
        ;   SWP_NOMOVE:=0x0002
        ;   SWP_NOREDRAW:=0x0008
        ;   SWP_NOACTIVATE:=0x0010
        ;   SWP_NOOWNERZORDER:=0x0200
        ;   SWP_NOSENDCHANGING:=0x0400
        ;   SWP_DEFERERASE:=0x2000
        ;   SWP_ASYNCWINDOWPOS:=0x4000
        DllCall("SetWindowPos", "ptr", this["hBTT" WhichToolTip], "ptr", -1, "int", 0, "int", 0, "int", 0, "int", 0, "uint", 26139)
      }

      ; 保存参数值,以便之后比对参数值是否改变
        this["SavedText" WhichToolTip]        := Text
      , this["SavedOptions" WhichToolTip]     := O.Checksum
      , this["SavedX" WhichToolTip]           := X   ; 这里的 X,Y 是经过 _CalculateDisplayPosition() 计算后的新 X,Y
      , this["SavedY" WhichToolTip]           := Y
      , this["SavedW" WhichToolTip]           := RectWithBorderWidth
      , this["SavedH" WhichToolTip]           := RectWithBorderHeight
      , this["SavedTargetHWND" WhichToolTip]  := O.TargetHWND
      , this["SavedCoordMode" WhichToolTip]   := O.CoordMode
      , this["SavedTransparent" WhichToolTip] := O.Transparent
    }
    ; x,y 任意一个跟随鼠标位置 或 使用窗口或客户区模式(窗口大小可能发生改变或者窗口发生移动)
    ; 或 目标窗口发生变化 或 坐标模式发生变化
    ; 或 整体透明度发生变化 这5种情况可能需要移动位置,需要进行坐标计算。
    else if ((X="" or Y="") or O.CoordMode!="Screen"
          or O.TargetHWND!=this.SavedTargetHWND or O.CoordMode!=this.SavedCoordMode
          or O.Transparent!=this.SavedTransparent)
    {
      ; 返回文本框不超出目标范围(比如屏幕范围)的最佳坐标。
      this._CalculateDisplayPosition(X, Y, this["SavedW" WhichToolTip], this["SavedH" WhichToolTip], O)
      ; 判断文本框 显示位置
      ; 或 显示透明度 是否发生改变
      if (X!=this["SavedX" WhichToolTip] or Y!=this["SavedY" WhichToolTip]
      or  O.Transparent!=this.SavedTransparent)
      {
        ; 显示
        UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip]
                          , X, Y, this["SavedW" WhichToolTip], this["SavedH" WhichToolTip], O.Transparent)
        ; 保存新的位置
          this["SavedX" WhichToolTip]           := X
        , this["SavedY" WhichToolTip]           := Y
        , this["SavedTargetHWND" WhichToolTip]  := O.TargetHWND
        , this["SavedCoordMode" WhichToolTip]   := O.CoordMode
        , this["SavedTransparent" WhichToolTip] := O.Transparent
      }
    }
    ret:={Hwnd : this["hBTT" WhichToolTip]
        , X    : X
        , Y    : Y
        , W    : this["SavedW" WhichToolTip]
        , H    : this["SavedH" WhichToolTip]}
    return ret
  }

  ; 为了统一参数的传输,以及特殊模式的设置,修改了 gdip 库的 Gdip_TextToGraphics() 函数。
  _TextToGraphics(pGraphics, Text, Options, Measure:=0)
  {
    static Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
    ; 设置字体样式
    Style := 0
    for eachStyle, valStyle in StrSplit(Styles, "|")
    {
      if InStr(Options.FontStyle, valStyle)
        Style |= (valStyle != "StrikeOut") ? (A_Index-1) : 8
    }
    if (FileExist(Options.Font))  ; 加载未安装的本地字体
    {
      hFontCollection := Gdip_NewPrivateFontCollection()
      hFontFamily := Gdip_CreateFontFamilyFromFile(Options.Font, hFontCollection)
    }
    if !isset(hFontFamily ) or !hFontFamily               ; 加载已安装的字体
      hFontFamily := Gdip_FontFamilyCreate(Options.Font)
    if !isset(hFontFamily ) or !hFontFamily                 ; 加载默认字体
      hFontFamily := Gdip_FontFamilyCreateGeneric(1)
    ; 根据 DPI 缩放比例自动调整字号
    hFont := Gdip_FontCreate(hFontFamily, Options.FontSize * Options.DPIScale, Style)
    ; 设置文字格式化样式,LineLimit = 0x00002000 只显示完整的行。
    ; 比如最后一行,因为布局高度有限,只能显示出一半,此时就会让它完全不显示。
    ; 直接使用 Gdip_StringFormatGetGeneric(1) 包含 LineLimit 设置,同时可以实现左右空白区域最小化。
    ; 但这样有个副作用,那就是无法精确的设置文本框的宽度了,同时最右边文字的间距会被压缩。
    ; 例如指定宽度800,可能返回的宽度是793,因为右边没有用空白补上。
    ; 好处是右边几乎没有空白区域,左边也没有,所以接近完美的实现文字居中了。
    ; hStringFormat := Gdip_StringFormatCreate(0x00002000)
    ; if !hStringFormat
      hStringFormat := Gdip_StringFormatGetGeneric(1)

    ; 准备文本画刷
    if (Options.TCLGA!="" and Options.TCLGM and Options.TCLGS and Options.TCLGE
        and Options.Width and Options.Height)             ; 渐变画刷
    {
      pBrush := this._CreateLinearGrBrush(Options.TCLGA, Options.TCLGM, Options.TCLGS, Options.TCLGE
                                        , this.NonNull_Ret(Options.HasOwnProp("X") ? Options.X : 0, 0), this.NonNull_Ret(Options.HasOwnProp("Y") ? Options.Y : 0, 0)
                                        , Options.Width, Options.Height)
    }
    else
      pBrush := Gdip_BrushCreateSolid(Options.TextColor)  ; 纯色画刷

    ; 检查参数是否齐全
    if !(hFontFamily && hFont && hStringFormat && pBrush && pGraphics)
    {
      E := !pGraphics ? -2 : !hFontFamily ? -3 : !hFont ? -4 : !hStringFormat ? -5 : !pBrush ? -6 : 0
      if pBrush
        Gdip_DeleteBrush(pBrush)
      if hStringFormat
        Gdip_DeleteStringFormat(hStringFormat)
      if hFont
        Gdip_DeleteFont(hFont)
      if hFontFamily
        Gdip_DeleteFontFamily(hFontFamily)
      if hFontCollection
        Gdip_DeletePrivateFontCollection(hFontCollection)
      return E
    }

    TabStops := []
    for k, v in Options.TabStops
      TabStops.Push(v * Options.DPIScale)
    Gdip_SetStringFormatTabStops(hStringFormat, TabStops)                      ; 设置 TabStops
    Gdip_SetStringFormatAlign(hStringFormat, Align:=0)                         ; 设置左对齐
    Gdip_SetTextRenderingHint(pGraphics, Options.FontRender)                   ; 设置渲染模式
    ;=CreateRectF(RC
    CreateRectF(&RC
              , Options.HasOwnProp("X") ? this.NonNull_Ret(Options.X, 0) : 0                                ; x,y 需要至少为0
              , Options.HasOwnProp("Y") ? this.NonNull_Ret(Options.Y, 0) : 0
              , Options.Width, Options.Height)                                 ; 宽高可以为空
    ;=returnRC := Gdip_MeasureString(pGraphics, Text, hFont, hStringFormat, RC)  ; 计算大小
    returnRC := Gdip_MeasureString(pGraphics, Text, hFont, hStringFormat, &RC)  ; 计算大小
    _E:=""    ;城西加
    if !Measure
      _E := Gdip_DrawString(pGraphics, Text, hFont, hStringFormat, pBrush, &RC)
    Gdip_DeleteBrush(pBrush)
    Gdip_DeleteFont(hFont)
    Gdip_DeleteStringFormat(hStringFormat)
    Gdip_DeleteFontFamily(hFontFamily)
    if Isset(hFontCollection) and hFontCollection
      Gdip_DeletePrivateFontCollection(hFontCollection)
    return _E ? _E : returnRC
  }

  _CreateLinearGrBrush(Angle, Mode, StartColor, EndColor, x, y, w, h)
  {
    ; Mode=8 Angle 0=左到右 90=上到下 180=右到左 270=下到上
    ; Mode=3 Angle 0=左到右 90=近似上到下
    ; Mode=4 Angle 0=左到右 90=下到上
    switch Mode
    {
      case 1,3,5,7:pBrush:=Gdip_CreateLinearGrBrush(x, y, x+w, y, StartColor, EndColor)
      case 2,4,6,8:pBrush:=Gdip_CreateLinearGrBrush(x, y+h//2, x+w, y+h//2, StartColor, EndColor)
    }
    switch Mode
    {
      case 1,2: Gdip_RotateLinearGrBrushTransform(pBrush, Angle, 0)  ; 性能比模式3、4高10倍左右
      case 3,4: Gdip_RotateLinearGrBrushTransform(pBrush, Angle, 1)
      case 5,6: Gdip_RotateLinearGrBrushAtCenter(pBrush, Angle, 0)
      case 7,8: Gdip_RotateLinearGrBrushAtCenter(pBrush, Angle, 1)   ; 可绕中心旋转
    }
    return pBrush
  }

  ; 此函数确保传入空值或者错误值均可返回正确值。
  _CheckStylesAndOptions(Styles, Options)
  {
    O := {}
    if Styles=""
    {
      O:={Border:1,Rounded:3,Margin:5,TabStops:[50],TextColor:0xff575757,BackgroundColor:0xffffffff,Font:this.ToolTipFontName,FontSize:12,FontRender:5,FontStyle:"",BCLGS:"",BCLGE:"",BCLGA:"",BCLGM:"",TCLGS:"",TCLGE:"",TCLGA:""
      ,TCLGM:"",BorderColor:0xff575757}

    }
    Else
    {
        O.Border          :=Styles.HasOwnProp("Border") ?  this.NonNull_Ret(Styles.Border         , 1                   , 0 , 20) : 1  ; 细边框    默认1 0-20
      , O.Rounded         :=Styles.HasOwnProp("Rounded") ?   this.NonNull_Ret(Styles.Rounded        , 3                   , 0 , 30) : 3 ; 圆角      默认3 0-30
      , O.Margin          :=Styles.HasOwnProp("Margin") ?   this.NonNull_Ret(Styles.Margin         , 5                   , 0 , 30) : 5  ; 边距      默认5 0-30
      , O.TabStops        :=Styles.HasOwnProp("TabStops") ?   this.NonNull_Ret(Styles.TabStops       , [50]                , "", "") : [50] ; 制表符宽  默认[50]
      , O.TextColor       :=Styles.HasOwnProp("TextColor") ?   this.NonNull_Ret(Styles.TextColor      , 0xff575757          , "", "") : 0xff575757 ; 文本色    默认0xff575757
      , O.BackgroundColor :=Styles.HasOwnProp("BackgroundColor") ?   this.NonNull_Ret(Styles.BackgroundColor, 0xffffffff          , "", "") : 0xffffffff ; 背景色    默认0xffffffff
      , O.Font            :=Styles.HasOwnProp("Font") ?   this.NonNull_Ret(Styles.Font           , this.ToolTipFontName, "", "") : this.ToolTipFontName ; 字体      默认与 ToolTip 一致
      , O.FontSize        :=Styles.HasOwnProp("FontSize") ?   this.NonNull_Ret(Styles.FontSize       , 12                  , "", "") : 12 ; 字号      默认12
      , O.FontRender      :=Styles.HasOwnProp("FontRender") ?   this.NonNull_Ret(Styles.FontRender     , 5                   , 0 , 5 ) : 5  ; 渲染模式  默认5 0-5
      , O.FontStyle       :=Styles.HasOwnProp("FontStyle") ?   Styles.FontStyle  : ""                                                      ; 字体样式  默认无

      ; 名字太长,建个缩写副本。
      , O.BCLGS  :=Styles.HasOwnProp("BorderColorLinearGradientStart") ?   Styles.BorderColorLinearGradientStart : ""                                           ; 细边框渐变色    默认无
      , O.BCLGE  :=Styles.HasOwnProp("BorderColorLinearGradientEnd") ?   Styles.BorderColorLinearGradientEnd  : ""                                           ; 细边框渐变色    默认无
      , O.BCLGA  :=Styles.HasOwnProp("BorderColorLinearGradientAngle") ?   Styles.BorderColorLinearGradientAngle : ""                                         ; 细边框渐变角度  默认无
      , O.BCLGM  :=Styles.HasOwnProp("BorderColorLinearGradientMode") ?   this.NonNull_Ret(Styles.BorderColorLinearGradientMode, "", 1, 8) : ""                 ; 细边框渐变模式  默认无 1-8

      ; 名字太长,建个缩写副本。
      , O.TCLGS  :=Styles.HasOwnProp("TextColorLinearGradientStart") ?   Styles.TextColorLinearGradientStart : ""                                             ; 文本渐变色      默认无
      , O.TCLGE  :=Styles.HasOwnProp("TextColorLinearGradientEnd") ?   Styles.TextColorLinearGradientEnd : ""                                               ; 文本渐变色      默认无
      , O.TCLGA  :=Styles.HasOwnProp("TextColorLinearGradientAngle") ?   Styles.TextColorLinearGradientAngle : ""                                             ; 文本渐变角度    默认无
      , O.TCLGM  :=Styles.HasOwnProp("TextColorLinearGradientMode") ?   this.NonNull_Ret(Styles.TextColorLinearGradientMode, "", 1, 8) : ""                  ; 文本渐变模式    默认无 1-8

      ; 名字太长,建个缩写副本。
      , O.BGCLGS :=Styles.HasOwnProp("BackgroundColorLinearGradientStart") ?   Styles.BackgroundColorLinearGradientStart : ""                                       ; 背景渐变色      默认无
      , O.BGCLGE :=Styles.HasOwnProp("BackgroundColorLinearGradientEnd") ?   Styles.BackgroundColorLinearGradientEnd  : ""                                        ; 背景渐变色      默认无
      , O.BGCLGA :=Styles.HasOwnProp("BackgroundColorLinearGradientAngle") ?   Styles.BackgroundColorLinearGradientAngle : ""                                       ; 背景渐变角度    默认无
      , O.BGCLGM :=Styles.HasOwnProp("BackgroundColorLinearGradientMode") ?   this.NonNull_Ret(Styles.BackgroundColorLinearGradientMode, "", 1, 8) : ""            ; 背景渐变模式    默认无 1-8

      ; a:=0xaabbccdd 下面是运算规则
      ; a>>16    = 0xaabb
      ; a>>24    = 0xaa
      ; a&0xffff = 0xccdd
      ; a&0xff   = 0xdd
      ; 0x88<<16 = 0x880000
      ; 0x880000+0xbbcc = 0x88bbcc

      ;;;, BlendedColor2 := (O.TCLGS and O.TCLGE and O.TCLGD) ? O.TCLGS : O.TextColor                   ; 使用文本渐变色替换文本色用于混合
      , BlendedColor2 := (O.TCLGS and O.TCLGE) ? O.TCLGS : O.TextColor                   ; 使用文本渐变色替换文本色用于混合
      , BlendedColor  := ((O.BackgroundColor>>24)<<24) + (BlendedColor2&0xffffff)                    ; 混合色    背景色的透明度与文本色混合
      , O.BorderColor :=Styles.HasOwnProp("BorderColor") ?   this.NonNull_Ret(Styles.BorderColor , BlendedColor      , "", "") :  BlendedColor          ; 细边框色  默认混合色
    }
    if Options=""
    {
        O.TargetHWND:=WinExist("A")
      , O.CoordMode:=A_CoordModeToolTip
      , O.Transparent:=255
      , O.MouseNeverCoverToolTip:=1
      , O.DistanceBetweenMouseXAndToolTip:=16
      , O.DistanceBetweenMouseYAndToolTip:=16
      , O.JustCalculateSize:=""
    }
    Else
    {
      O.TargetHWND  :=Options.HasOwnProp("TargetHWND") ?   this.NonNull_Ret(Options.TargetHWND , WinExist("A")     , "", "") : WinExist("A")       ; 目标句柄    默认活动窗口
      , O.CoordMode   :=Options.HasOwnProp("CoordMode") ?   this.NonNull_Ret(Options.CoordMode  , A_CoordModeToolTip, "", "")  : A_CoordModeToolTip        ; 坐标模式    默认与 ToolTip 一致
      , O.Transparent :=Options.HasOwnProp("Transparent") ?   this.NonNull_Ret(Options.Transparent, 255               , 0 , 255) : 255          ; 整体透明度  默认255
      , O.MouseNeverCoverToolTip          :=Options.HasOwnProp("MouseNeverCoverToolTip") ?    this.NonNull_Ret(Options.MouseNeverCoverToolTip         , 1 , 0 , 1 ) : 1  ; 鼠标永不遮挡文本框
      , O.DistanceBetweenMouseXAndToolTip :=Options.HasOwnProp("DistanceBetweenMouseXAndToolTip") ?    this.NonNull_Ret(Options.DistanceBetweenMouseXAndToolTip, 16, "", "")  : 16  ; 鼠标与文本框的X距离
      , O.DistanceBetweenMouseYAndToolTip :=Options.HasOwnProp("DistanceBetweenMouseYAndToolTip") ?    this.NonNull_Ret(Options.DistanceBetweenMouseYAndToolTip, 16, "", "")  : 16  ; 鼠标与文本框的Y距离
      , O.JustCalculateSize               :=Options.HasOwnProp("JustCalculateSize") ?    Options.JustCalculateSize  : ""                                             ; 仅计算显示尺寸并返回
    }
    ; 难以比对两个对象是否一致,所以造一个变量比对。
    ; 这里的校验因素,必须是那些改变后会使画面内容也产生变化的因素。
    ; 所以没有 TargetHWND 和 CoordMode 和 Transparent ,因为这三个因素只影响位置。
    for k, v in O.TabStops
      TabStops .= v ","
    O.Checksum := O.Border          "|" O.Rounded  "|" O.Margin     "|" TabStops    "|"
                . O.BorderColor     "|" O.BCLGS    "|" O.BCLGE      "|" O.BCLGA     "|" O.BCLGM  "|"
                . O.TextColor       "|" O.TCLGS    "|" O.TCLGE      "|" O.TCLGA     "|" O.TCLGM  "|"
                . O.BackgroundColor "|" O.BGCLGS   "|" O.BGCLGE     "|" O.BGCLGA    "|" O.BGCLGM "|"
                . O.Font            "|" O.FontSize "|" O.FontRender "|" O.FontStyle
    return O
  }

  ; 此函数确保文本框显示位置不会超出目标范围。
  ; 使用 ByRef X, ByRef Y 返回不超限的位置。
  _CalculateDisplayPosition(&X, &Y, W, H, Options, GetTargetSize:=0)
  {
      Point := Buffer(8, 0) 
    ; 获取鼠标位置
    , DllCall("GetCursorPos", "Ptr", Point.Ptr, "Int")
    , MouseX := NumGet(Point, 0, "Int"), MouseY := NumGet(Point, 4, "Int")

    ; x,y 即 ToolTip 显示的位置。
    ; x,y 同时为空表明完全跟随鼠标。
    ; x,y 单个为空表明只跟随鼠标横向或纵向移动。
    ; x,y 都有值,则说明被钉在屏幕或窗口或客户区的某个位置。
    ; MouseX,MouseY 是鼠标的屏幕坐标。
    ; DisplayX,DisplayY 是 x,y 经过转换后的屏幕坐标。
    ; 以下过程 x,y 不发生变化, DisplayX,DisplayY 储存转换好的屏幕坐标。
    ; 不要尝试合并分支 (X="" and Y="") 与 (A_CoordModeToolTip = "Screen")。
    ; 因为存在把坐标模式设为 Window 或 Client 但又同时不给出 x,y 的情况!!!!!!
    if (X="" and Y="")
    { ; 没有给出 x,y 则使用鼠标坐标
        DisplayX     := MouseX
      , DisplayY     := MouseY
      ; 根据坐标判断在第几个屏幕里,并获得对应屏幕边界。
      ; 使用 MONITOR_DEFAULTTONEAREST 设置,可以在给出的点不在任何显示器内时,返回距离最近的显示器。
      ; 这样可以修正使用 1920,1080 这种错误的坐标,导致返回空值,导致画图失败的问题。
      ; 为什么 1920,1080 是错误的呢?因为 1920 是宽度,而坐标起点是0,所以最右边坐标值是 1919,最下面是 1079。
      ;=, hMonitor     := MDMF_FromPoint(DisplayX, DisplayY, MONITOR_DEFAULTTONEAREST:=2)
      , hMonitor     := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2)
      , TargetLeft   := this.Monitors[hMonitor].Left
      , TargetTop    := this.Monitors[hMonitor].Top
      , TargetRight  := this.Monitors[hMonitor].Right
      , TargetBottom := this.Monitors[hMonitor].Bottom
      , TargetWidth  := TargetRight-TargetLeft
      , TargetHeight := TargetBottom-TargetTop
      ; 将对应屏幕的 DPIScale 存入 Options 中。
      , Options.DPIScale := this.Monitors[hMonitor].DPIScale
    }
      ; 已给出 x和y 或x 或y,都会走到下面3个分支去。
    else if (Options.CoordMode  = "Window" or Options.CoordMode  = "Relative")
    { ; 已给出 x或y 且使用窗口坐标
        WinGetPos &WinX, &WinY, &WinW, &WinH,"ahk_id " Options.TargetHWND

        XInScreen    := WinX+X
      , YInScreen    := WinY+Y
      , TargetLeft   := WinX
      , TargetTop    := WinY
      , TargetWidth  := WinW
      , TargetHeight := WinH
      , TargetRight  := TargetLeft+TargetWidth
      , TargetBottom := TargetTop+TargetHeight
      , DisplayX     := (X="") ? MouseX : XInScreen
      , DisplayY     := (Y="") ? MouseY : YInScreen
      , hMonitor     := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2)
      , Options.DPIScale := this.Monitors[hMonitor].DPIScale
    }
    else if (Options.CoordMode  = "Client")
    { ; 已给出 x或y 且使用客户区坐标
        ClientArea:=Buffer(16, 0)
      , DllCall("GetClientRect", "Ptr", Options.TargetHWND, "Ptr", ClientArea.Ptr)
      , DllCall("ClientToScreen", "Ptr", Options.TargetHWND, "Ptr", ClientArea.Ptr)
      , ClientX      := NumGet(ClientArea, 0, "Int")
      , ClientY      := NumGet(ClientArea, 4, "Int")
      , ClientW      := NumGet(ClientArea, 8, "Int")
      , ClientH      := NumGet(ClientArea, 12, "Int")

        XInScreen    := ClientX+ (X ? X : 0)
      , YInScreen    := ClientY+ (X ? y : 0)
      , TargetLeft   := ClientX
      , TargetTop    := ClientY
      , TargetWidth  := ClientW
      , TargetHeight := ClientH
      , TargetRight  := TargetLeft+TargetWidth
      , TargetBottom := TargetTop+TargetHeight
      , DisplayX     := (X="") ? MouseX : XInScreen
      , DisplayY     := (Y="") ? MouseY : YInScreen
      , hMonitor     := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2)
      , Options.DPIScale := this.Monitors[hMonitor].DPIScale
    }
    else ; 这里必然 A_CoordModeToolTip = "Screen"
    { ; 已给出 x或y 且使用屏幕坐标
        DisplayX     := (X="") ? MouseX : X
      , DisplayY     := (Y="") ? MouseY : Y
      , hMonitor     := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2)
      , TargetLeft   := this.Monitors[hMonitor].Left
      , TargetTop    := this.Monitors[hMonitor].Top
      , TargetRight  := this.Monitors[hMonitor].Right
      , TargetBottom := this.Monitors[hMonitor].Bottom
      , TargetWidth  := TargetRight-TargetLeft
      , TargetHeight := TargetBottom-TargetTop
      , Options.DPIScale := this.Monitors[hMonitor].DPIScale
    }

    if (GetTargetSize=1)
    {
        TargetSize   := []
      , TargetSize.X := TargetLeft
      , TargetSize.Y := TargetTop
      ; 一个窗口,有各种各样的方式可以让自己的高宽超过屏幕高宽。
      ; 例如最大化的时候,看起来刚好填满了屏幕,应该是1920*1080,但实际获取会发现是1936*1096。
      ; 还可以通过拖动窗口边缘调整大小的方式,让它变1924*1084。
      ; 还可以直接在创建窗口的时候,指定一个数值,例如3000*3000。
      ; 由于设计的时候, DIB 最大就是多个屏幕大小的总和。
      ; 当造出一个超过屏幕大小总和的窗口,又使用了 A_CoordModeToolTip = "Window" 之类的参数,同时待显示文本单行又超级长。
      ; 此时 (显示宽高 = 窗口宽高) > DIB宽高,会导致 UpdateLayeredWindow() 显示失败。
      ; 所以这里做一下限制。
      , TargetSize.W := Min(TargetWidth, this.DIBWidth)
      , TargetSize.H := Min(TargetHeight, this.DIBHeight)
      return TargetSize
    }

      DPIScale := Options.DPIScale
    ; 为跟随鼠标显示的文本框增加一个距离,避免鼠标和文本框挤一起发生遮挡。
    ; 因为前面需要用到原始的 DisplayX 和 DisplayY 进行计算,所以在这里才增加距离。
    , DisplayX := (X="") ? DisplayX+Options.DistanceBetweenMouseXAndToolTip*DPIScale : DisplayX
    , DisplayY := (Y="") ? DisplayY+Options.DistanceBetweenMouseYAndToolTip*DPIScale : DisplayY

    ; 处理目标边缘(右和下)的情况,让文本框可以贴边显示,不会超出目标外。
    , DisplayX := (DisplayX+W>=TargetRight)  ? TargetRight-W  : DisplayX
    , DisplayY := (DisplayY+H>=TargetBottom) ? TargetBottom-H : DisplayY
    ; 处理目标边缘(左和上)的情况,让文本框可以贴边显示,不会超出目标外。
    ; 不建议合并代码,理解会变得困难。
    , DisplayX := (DisplayX<TargetLeft) ? TargetLeft : DisplayX
    , DisplayY := (DisplayY<TargetTop)  ? TargetTop  : DisplayY

    ; 处理鼠标遮挡文本框的情况(即鼠标跑到文本框坐标范围内了)。这里要做测试的话,需要测试5种情况。
    ; X跟随 Y跟随。
    ; X跟随 Y固定。0和1919都要测
    ; Y跟随 X固定。0和1079都要测
    ; X固定 Y固定。此种情况文本框可被鼠标遮挡,无需测试。
    if  (Options.MouseNeverCoverToolTip=1
    and (X="" or Y="")
    and MouseX>=DisplayX and MouseY>=DisplayY and MouseX<=DisplayX+W and MouseY<=DisplayY+H)
    {
      ; MouseY-H-16 是往上弹,应对在左下角和右下角的情况。
      ; MouseY+H+16 是往下弹,应对在右上角和左上角的情况。
      ; 这里不要去用 Abs(Options.DistanceBetweenMouseYAndToolTip) 替代 16。因为当前者很大时,显示效果不好。
      ; 优先往上弹,如果不超限,则上弹。如果超限则往下弹,下弹超限,则不弹。
      DisplayY := MouseY-H-16>=TargetTop ? MouseY-H-16 : MouseY+H+16<=TargetBottom ? MouseY+16 : DisplayY
    }

    ; 使用 ByRef 变量特性返回计算得到的 X和Y
    X := DisplayX , Y := DisplayY
  }
  
  ; https://autohotkey.com/boards/viewtopic.php?f=6&t=4379
  ; jballi's Fnt Library
  Fnt_GetTooltipFontName()
  {
    static LF_FACESIZE:=32  ;-- In TCHARS
    return StrGet(this.Fnt_GetNonClientMetrics()+316+28,LF_FACESIZE)
  }

  Fnt_GetNonClientMetrics()
  {
    static Dummy15105062
      ,SPI_GETNONCLIENTMETRICS:=0x29
      ,NONCLIENTMETRICS

    ;-- Set the size of NONCLIENTMETRICS structure
    cbSize:=500
    if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0)  ;-- Vista+
      cbSize+=4

    ;-- Create and initialize NONCLIENTMETRICS structure
    NONCLIENTMETRICS := Buffer(cbSize, 0) 
    NumPut("UInt", cbSize, NONCLIENTMETRICS, 0)

    ;-- Get nonclient metrics parameter
    if !DllCall("SystemParametersInfo"
      ,"UInt",SPI_GETNONCLIENTMETRICS
      ,"UInt",cbSize
      ;,"Ptr",&NONCLIENTMETRICS
      ,"Ptr",NONCLIENTMETRICS.Ptr
      ,"UInt",0)
      return false

    ;-- Return to sender
    return NONCLIENTMETRICS.Ptr
  }
  
    ; 变量为空,则使用默认值。变量不为空,则使用变量值。
  ; 同时可以检查变量是否超出最大最小范围。
  ; 注意,默认值不受最大最小范围的限制。
  ; 也就是说
  ; 当变量值为"",默认值为8,范围为2-5,此时变量值会是8。
  ; 当变量值为10,默认值为8,范围为2-5,此时变量值会是5。
  NonNull(&var, DefaultValue, MinValue:="", MaxValue:="")		; 237ms
  {
    var:= var="" ? DefaultValue : MinValue="" ? (MaxValue="" ? var : Min(var, MaxValue)) : (MaxValue!="" ? Max(Min(var, MaxValue), MinValue) : Max(var, MinValue))
  }

  ; 与 NonNull 一致,区别是通过 return 返回值,而不是 ByRef。
  NonNull_Ret(var, DefaultValue, MinValue:="", MaxValue:="")			; 237ms
  {
    return var="" ? DefaultValue : MinValue="" ? (MaxValue="" ? var : Min(var, MaxValue)) : (MaxValue!="" ? Max(Min(var, MaxValue), MinValue) : Max(var, MinValue))
  }
  
}

;================================GDIP================================================
Gdip_Startup()
{
  DllCall("LoadLibrary", "str", "gdiplus")
  si := Buffer(A_PtrSize = 8 ? 24 : 16, 0)
  NumPut("UInt", 1, si)
  DllCall("gdiplus\GdiplusStartup", "UPtr*", &pToken:=0, "UPtr", si.Ptr, "UPtr", 0)
  if (!pToken) {
    throw Error("Gdiplus failed to start. Please ensure you have gdiplus on your system")
  }
  return pToken
}

MDMF_Enum(HMON := "") {
  static EnumProc := CallbackCreate(MDMF_EnumProc)
  static Obj := "Map"
  static Monitors := {}
  if (HMON = "") { 	; new enumeration
    Monitors := %Obj%("TotalCount", 0)
    if !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", ObjPtr(Monitors), "Int")
      return False
  }
  return (HMON = "") ? Monitors : Monitors.Has(HMON) ? Monitors[HMON] : False
}

CreateDIBSection(w, h, hdc:="", bpp:=32, &ppvBits:=0)
{
  hdc2 := hdc ? hdc : GetDC()
  bi := Buffer(40, 0)
  NumPut("UInt", w, bi, 4)
  , NumPut("UInt", h, bi, 8)
  , NumPut("UInt", 40, bi, 0)
  , NumPut("ushort", 1, bi, 12)
  , NumPut("uInt", 0, bi, 16)
  , NumPut("ushort", bpp, bi, 14)
  hbm := DllCall("CreateDIBSection"
          , "UPtr", hdc2
          , "UPtr", bi.Ptr
          , "UInt", 0
          , "UPtr*", &ppvBits
          , "UPtr", 0
          , "UInt", 0, "UPtr")

  if (!hdc) {
    ReleaseDC(hdc2)
  }
  return hbm
}

CreateCompatibleDC(hdc:=0)
{
  return DllCall("CreateCompatibleDC", "UPtr", hdc)
}

Gdip_GraphicsFromHDC(hdc)
{
  DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", &pGraphics:=0)
  return pGraphics
}

Gdip_SetSmoothingMode(pGraphics, SmoothingMode)
{
    if !pGraphics
        return 2
  return DllCall("gdiplus\GdipSetSmoothingMode", "UPtr", pGraphics, "Int", SmoothingMode)
}

Gdip_SetPixelOffsetMode(graphics, pixelOffsetMode) => DllCall('Gdiplus\GdipSetPixelOffsetMode', 'ptr', graphics, 'ptr', pixelOffsetMode, 'uint')

SelectObject(hdc, hgdiobj)
{
    return DllCall("SelectObject", "UPtr", hdc, "UPtr", hgdiobj)
}

Gdip_DeleteGraphics(pGraphics)
{
  return DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
}

DeleteObject(hObject)
{
  return DllCall("DeleteObject", "UPtr", hObject)
}

DeleteDC(hdc)
{
  return DllCall("DeleteDC", "UPtr", hdc)
}

Gdip_Shutdown(pToken)
{
  DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
  if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "UPtr") {
    DllCall("FreeLibrary", "UPtr", hModule)
  }
  return 0
}

Gdip_GraphicsClear(pGraphics, ARGB:=0x00ffffff)
{
    If !pGraphics
      return 2   
  return DllCall("gdiplus\GdipGraphicsClear", "UPtr", pGraphics, "Int", ARGB)
}

UpdateLayeredWindow(hwnd, hdc, x:="", y:="", w:="", h:="", Alpha:=255)
{
  if ((x != "") && (y != ""))
    pt := Buffer(8), NumPut("UInt", x, pt, 0), NumPut("UInt", y, pt, 4)
  if (w = "") || (h = "")
  {
    CreateRect(&winRect:="", 0, 0, 0, 0) ;is 16 on both 32 and 64
    DllCall( "GetWindowRect", "UPtr", hwnd, "UPtr", winRect.Ptr )
    w := NumGet(winRect, 8, "UInt")  - NumGet(winRect, 0, "UInt")
    h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt")
  }
  return DllCall("UpdateLayeredWindow"
  , "UPtr", hwnd
  , "UPtr", 0
  , "UPtr", ((x = "") && (y = "")) ? 0 : pt.Ptr
  , "Int64*", w|h<<32
  , "UPtr", hdc
  , "Int64*", 0
  , "UInt", 0
  , "UInt*", Alpha<<16|1<<24
  , "UInt", 2)
}

Gdip_BrushCreateSolid(ARGB:=0xff000000)
{
  DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", &pBrush:=0)
  return pBrush
}

Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
{
  return DllCall("gdiplus\GdipFillRectangle"
          , "UPtr", pGraphics
          , "UPtr", pBrush
          , "Float", x
          , "Float", y
          , "Float", w
          , "Float", h)
}

Gdip_FillRoundedRectanglePath(pGraphics, pBrush, X, Y, W, H, R) {
   DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", &pPath:=0)
   D := (R * 2), W -= D, H -= D
   DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X, "Float", Y, "Float", D, "Float", D, "Float", 180, "Float", 90)
   DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X + W, "Float", Y, "Float", D, "Float", D, "Float", 270, "Float", 90)
   DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X + W, "Float", Y + H, "Float", D, "Float", D, "Float", 0, "Float", 90)
   DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X, "Float", Y + H, "Float", D, "Float", D, "Float", 90, "Float", 90)
   DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", pPath)
   RS := DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", pBrush, "Ptr", pPath)
   DllCall("Gdiplus.dll\GdipDeletePath", "Ptr", pPath)
   Return RS
}

Gdip_DeleteBrush(pBrush)
{
    return DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
}

Gdip_NewPrivateFontCollection(fontCollection:=0) => DllCall('Gdiplus\GdipNewPrivateFontCollection', 'ptr', fontCollection, 'uint')    

Gdip_CreateFontFamilyFromFile(FontFile, hFontCollection, FontName:="") {
   If !hFontCollection
      Return
   E := DllCall("gdiplus\GdipPrivateAddFontFile", "ptr", hFontCollection, "str", FontFile)
   if (FontName="" && !E)
   {
    pFontFamily := Buffer(10, 0) 
    DllCall("gdiplus\GdipGetFontCollectionFamilyList", "ptr", hFontCollection, "int", 1, "ptr", pFontFamily.Ptr, "int*", &found:=0)
    FontName:=Buffer(100, 0)
    DllCall("gdiplus\GdipGetFamilyName", "ptr", NumGet(pFontFamily, 0, "ptr"), "str", FontName, "ushort", 1033)
   }
   If !E
      DllCall("gdiplus\GdipCreateFontFamilyFromName", "str", FontName, "ptr", hFontCollection, "uint*", &hFontFamily:=0)
   Return hFontFamily
}

Gdip_FontFamilyCreate(Font)
{
    DllCall("gdiplus\GdipCreateFontFamilyFromName"
                    , "UPtr", StrPtr(Font)
                    , "UInt", 0
                    , "UPtr*", &hFamily:=0)
    return hFamily
}

Gdip_FontFamilyCreateGeneric(whichStyle) {
    If (whichStyle=0)
        DllCall("gdiplus\GdipGetGenericFontFamilyMonospace", "UPtr*", &hFontFamily:=0)
    Else If (whichStyle=1)
        DllCall("gdiplus\GdipGetGenericFontFamilySansSerif", "UPtr*", &hFontFamily:=0)
    Else If (whichStyle=2)
        DllCall("gdiplus\GdipGetGenericFontFamilySerif", "UPtr*", &hFontFamily:=0)
    Return hFontFamily
}

Gdip_FontCreate(hFamily, Size, Style:=0)
{
    DllCall("gdiplus\GdipCreateFont", "UPtr", hFamily, "Float", Size, "Int", Style, "Int", 0, "UPtr*", &hFont:=0)
    return hFont
}

Gdip_StringFormatGetGeneric(whichFormat:=0) {
    If (whichFormat=1)
       DllCall("gdiplus\GdipStringFormatGetGenericTypographic", "UPtr*", &hStringFormat:=0)
    Else
        DllCall("gdiplus\GdipStringFormatGetGenericDefault", "UPtr*", &hStringFormat:=0)
    Return hStringFormat
}

Gdip_DeleteStringFormat(hFormat)
{
    return DllCall("gdiplus\GdipDeleteStringFormat", "UPtr", hFormat)
}

Gdip_DeleteFont(hFont)
{
  return DllCall("gdiplus\GdipDeleteFont", "UPtr", hFont)
}

Gdip_DeleteFontFamily(hFamily)
{
  return DllCall("gdiplus\GdipDeleteFontFamily", "UPtr", hFamily)
}

Gdip_DeletePrivateFontCollection(fontCollection) => DllCall('Gdiplus\GdipDeletePrivateFontCollection', 'ptr', fontCollection, 'uint')

Gdip_SetStringFormatTabStops(format,tabStops)
{
    firstTabOffset:=0
    count:=tabStops.Length
    buf :=Buffer(4 * count), p :=buf.Ptr
    loop count 
      p := NumPut('Float',tabStops[A_index],p)
    return DllCall('Gdiplus\GdipSetStringFormatTabStops', 'ptr', format, 'int', firstTabOffset, 'int', count, 'ptr', buf, 'uint')
}

Gdip_SetStringFormatAlign(hFormat, Align)
{
    return DllCall("gdiplus\GdipSetStringFormatAlign", "UPtr", hFormat, "Int", Align)
}

Gdip_SetTextRenderingHint(pGraphics, RenderingHint)
{
    If !pGraphics
        Return 2
  return DllCall("gdiplus\GdipSetTextRenderingHint", "UPtr", pGraphics, "Int", RenderingHint)
}

CreateRectF(&RectF, x, y, w, h)
{
  RectF := Buffer(16)
  NumPut("Float", x, RectF, 0), NumPut("Float", y, RectF, 4), NumPut("Float", w, RectF, 8), NumPut("Float", h, RectF, 12)
}

Gdip_MeasureString(pGraphics, sString, hFont, hFormat, &RectF)
{
  RC := Buffer(16)
  DllCall("gdiplus\GdipMeasureString"
          , "UPtr", pGraphics
          , "UPtr", StrPtr(sString)
          , "Int", -1
          , "UPtr", hFont
          , "UPtr", RectF.Ptr
          , "UPtr", hFormat
          , "UPtr", RC.Ptr
          , "uint*", &Chars:=0
          , "uint*", &Lines:=0)

  return RC.Ptr ? NumGet(RC, 0, "Float") "|" NumGet(RC, 4, "Float") "|" NumGet(RC, 8, "Float") "|" NumGet(RC, 12, "Float") "|" Chars "|" Lines : 0
}

Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, &RectF)
{
  return DllCall("gdiplus\GdipDrawString"
          , "UPtr", pGraphics
          , "UPtr", StrPtr(sString)
          , "Int", -1
          , "UPtr", hFont
          , "UPtr", RectF.Ptr
          , "UPtr", hFormat
          , "UPtr", pBrush)
}

Gdip_CreateLinearGrBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode:=1) {
   Static Ptr := "UPtr"
   CreatePointF(&PointF1, x1, y1)
   CreatePointF(&PointF2, x2, y2)
   DllCall("gdiplus\GdipCreateLineBrush", Ptr, PointF1.Ptr, Ptr, PointF2.Ptr, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, "UPtr*", &pLinearGradientBrush:=0)
   return pLinearGradientBrush
}

Gdip_RotateLinearGrBrushTransform(pLinearGradientBrush, Angle, matrixOrder:=0) => DllCall("gdiplus\GdipRotateLineTransform", "UPtr", pLinearGradientBrush, "float", Angle, "int", matrixOrder)

Gdip_RotateLinearGrBrushAtCenter(pLinearGradientBrush, Angle, MatrixOrder:=1) {
  Rect := Gdip_GetLinearGrBrushRect(pLinearGradientBrush) 
  cX := Rect.x + (Rect.w / 2)
  cY := Rect.y + (Rect.h / 2)
  pMatrix := Gdip_CreateMatrix()
  Gdip_TranslateMatrix(pMatrix, -cX , -cY)
  Gdip_RotateMatrix(pMatrix, Angle, MatrixOrder)
  Gdip_TranslateMatrix(pMatrix, cX, cY, MatrixOrder)
  E := Gdip_SetLinearGrBrushTransform(pLinearGradientBrush, pMatrix)
  Gdip_DeleteMatrix(pMatrix)
  Return E
}

MDMF_FromPoint(&X:="", &Y:="", Flag:=0) {
    if (X = "") || (Y = "") {
        PT := Buffer(8, 0)
        DllCall("User32.dll\GetCursorPos", "Ptr", PT.Ptr, "Int")
        if (X = "") {
            X := NumGet(PT, 0, "Int")
        }
        if (Y = "") {
            Y := NumGet(PT, 4, "Int")
        }
    }
    return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", Flag, "Ptr")
}

GetDC(hwnd:=0)
{
  return DllCall("GetDC", "UPtr", hwnd)
}

ReleaseDC(hdc, hwnd:=0)
{
  return DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
}

Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
{
  return DllCall("gdiplus\GdipSetInterpolationMode", "UPtr", pGraphics, "Int", InterpolationMode)
}

Gdip_SetPageUnit(graphics, unit) => DllCall('Gdiplus\GdipSetPageUnit', 'ptr', graphics, 'uint', unit, 'uint')

Gdip_SetCompositingQuality(graphics, compositingQuality) => DllCall('Gdiplus\GdipSetCompositingQuality', 'ptr', graphics, 'ptr', compositingQuality, 'uint')

GetWindowRect(hwnd, &W, &H) {
   If !hwnd
      Return
    rect := Buffer(16, 0)
   er := DllCall("dwmapi\DwmGetWindowAttribute"
      , "UPtr", hWnd  ; HWND  hwnd
      , "UInt", 9     ; DWORD dwAttribute (DWMWA_EXTENDED_FRAME_BOUNDS)
      , "UPtr", rect.Ptr ; PVOID pvAttribute
      , "UInt", rect.size  ; DWORD cbAttribute
      , "UInt")       ; HRESULT
   If er
      DllCall("GetWindowRect", "UPtr", hwnd, "UPtr", rect.Ptr, "UInt")
   r := Object()
   r.x1 := NumGet(rect, 0, "Int"), r.y1 := NumGet(rect, 4, "Int")
   r.x2 := NumGet(rect, 8, "Int"), r.y2 := NumGet(rect, 12, "Int")
   r.w := Abs(max(r.x1, r.x2) - min(r.x1, r.x2))
   r.h := Abs(max(r.y1, r.y2) - min(r.y1, r.y2))
   W := r.w
   H := r.h
   Return r
}

CreatePointF(&PointF, x, y)
{
    PointF := Buffer(8)
    NumPut("Float", x, PointF, 0), NumPut("Float", y, PointF, 4)
}

Gdip_GetLinearGrBrushRect(pLinearGradientBrush) {
  RectF := Buffer(16, 0)
  E := DllCall("gdiplus\GdipGetLineRect", "UPtr", pLinearGradientBrush, "UPtr", RectF.Ptr)
  If (!E) {
      rData := Object()
      rData.x := NumGet(RectF, 0, "float")
      rData.y := NumGet(RectF, 4, "float")
      rData.w := NumGet(RectF, 8, "float")
      rData.h := NumGet(RectF, 12, "float")
      Return rData
  } Else {
      Return E
  }
}

Gdip_CreateMatrix()
{
    DllCall("gdiplus\GdipCreateMatrix", "UPtr*", &Matrix:=0)
    return Matrix
}

Gdip_TranslateMatrix(matrix, offsetX, offsetY, order:=0) => DllCall('Gdiplus\GdipTranslateMatrix', 'ptr', matrix, 'int', offsetX, 'int', offsetY, 'uint', order, 'uint')

Gdip_RotateMatrix(matrix, angle, order:=0) => DllCall('Gdiplus\GdipRotateMatrix', 'ptr', matrix, 'int', angle, 'uint', order, 'uint')

Gdip_SetLinearGrBrushTransform(pLinearGradientBrush, pMatrix) => DllCall("gdiplus\GdipSetLineTransform", "UPtr", pLinearGradientBrush, "UPtr", pMatrix)

Gdip_DeleteMatrix(Matrix)
{
    return DllCall("gdiplus\GdipDeleteMatrix", "UPtr", Matrix)
}

MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
  Monitors := objfromptraddref(ObjectAddr)
  Monitors[HMON] := MDMF_GetInfo(HMON)
  Monitors["TotalCount"]++
  if (Monitors[HMON].Primary) {
    Monitors["Primary"] := HMON
  }
  return true
}

MDMF_GetInfo(HMON) {
  MIEX := Buffer(40 + (32 << !!1))
  NumPut("UInt", MIEX.Size, MIEX, 0)
  if DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", MIEX.Ptr, "Int") {
    return {Name:      (Name := StrGet(MIEX.Ptr + 40, 32))  ; CCHDEVICENAME = 32
          , Num:       RegExReplace(Name, ".*(\d+)$", "$1")
          , Left:      NumGet(MIEX, 4, "Int")    ; display rectangle
          , Top:       NumGet(MIEX, 8, "Int")    ; "
          , Right:     NumGet(MIEX, 12, "Int")   ; "
          , Bottom:    NumGet(MIEX, 16, "Int")   ; "
          , WALeft:    NumGet(MIEX, 20, "Int")   ; work area
          , WATop:     NumGet(MIEX, 24, "Int")   ; "
          , WARight:   NumGet(MIEX, 28, "Int")   ; "
          , WABottom:  NumGet(MIEX, 32, "Int")   ; "
          , Primary:   NumGet(MIEX, 36, "UInt")} ; contains a non-zero value for the primary monitor.
  }
  return False
}

CreateRect(&Rect, x, y, w, h)
{
  Rect := Buffer(16)
  NumPut("UInt", x, Rect, 0), NumPut("UInt", y, Rect, 4), NumPut("UInt", w, Rect, 8), NumPut("UInt", h, Rect, 12)
}

;=======================================Gdip与BTT完===============================================================

 

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