[AHK2] 屏幕放大镜

发布时间 2023-08-23 18:14:18作者: 落寞的雪

介绍

此脚本源自远古的ahk1,里面甚至使用了IfLess这种语法。
但不管怎么说,它是个十分好的脚本,代码精简,效率也好。所以我将它升级到了ahk2版,并对部分内容做提炼,疏通了整个脚本的脉络(老语法实在混乱)。

它的作用就是可以放大鼠标下的屏幕,以gui的形式展示出来。效果如下:

image

此外,还有以下特点:

  • 支持gui尺寸拖拽
  • 支持暂停
  • 支持缩放
  • 支持抗锯齿(仅缩小时有用)
  • 支持更多扩展
  • 放大十分清晰
  • 几乎无延迟

脚本

ahk2

热键

; #x 退出脚本
; #p 或 MButton 暂停画面(非暂停脚本)
; ^+WhellDown\WhellUp 缩放
; #a 抗锯齿

代码

依旧是将所有可配置项放在开头的config类内,其余功能可自行扩展。

#Requires AutoHotkey v2.0
#SingleInstance Force

CoordMode 'Mouse', 'Screen'

class Config {

  static initialZoom := 2 ; initial magnification, 1..32

  static guiOption := '+AlwaysOnTop +Resize'
  static guiPosX := 60
  static guiPosY := 0
  static guiSize := 3
}

zoom := Config.initialZoom
Rx := 128 ; half vertical/horizontal side of magnifier window
Ry := 128
Zx := Rx / zoom ; frame x/y size
Zy := Ry / zoom

OnUpdate(GuiObj, MinMax, w, h) {
  global
  Rx := w / 2
  Ry := h / 2
  Zx := Rx / zoom
  Zy := Ry / zoom
}

#x::
OnClose(*) {
  DllCall("gdi32.dll\DeleteDC", 'UInt', hdc_frame)
  DllCall("gdi32.dll\DeleteDC", 'UInt', hdd_frame)
  ExitApp()
}

g := Gui(Config.guiOption)
g.OnEvent('Size', OnUpdate)
g.OnEvent('Close', OnClose)
g.Show('w' Config.guiSize * Rx ' h' Config.guiSize * Ry 'x' Config.guiPosX ' y' Config.guiPosY)

MagnifierID := g.Hwnd
PrintSourceID := 0

hdd_frame := DllCall("GetDC", 'UInt', PrintSourceID)
hdc_frame := DllCall("GetDC", 'UInt', MagnifierID)

SetTimer Repaint, 5

GetClosest := (x, a, b) => x < a ? a : (b < x ? b : x)

Repaint() {
  global
  MouseGetPos(&x, &y)
  xz := GetClosest(x - Zx - 6, 0, A_ScreenWidth - 2 * Zx) ; keep the frame on screen
  yz := GetClosest(y - Zy - 6, 0, A_ScreenHeight - 2 * Zy)
  DllCall("gdi32.dll\StretchBlt", 'UInt', hdc_frame, 'Int'
    , 0, 'Int', 0, 'Int', 2 * Rx, 'Int', 2 * Ry
    , 'UInt', hdd_frame, 'UInt', xz, 'UInt', yz
    , 'Int', 2 * Zx, 'Int', 2 * Zy, 'UInt', 0xCC0020) ; SRCCOPY
}

#a::
{
  static antialize := 0
  DllCall("gdi32.dll\SetStretchBltMode", "uint", hdc_frame, "int", 4 * antialize)  ; Antializing ?
  antialize := !antialize
}

#p::
MButton::
{
  static paused := true
  paused
    ? SetTimer(Repaint, 0)
    : SetTimer(Repaint, 5)
  paused := !paused
}

^+WheelUp::
^+WheelDown::
{
  global
  if zoom < 31 and (A_ThisHotKey = "^+WheelUp")
    zoom *= 1.189207115         ; sqrt(sqrt(2))
  if zoom > 1 and (A_ThisHotKey = "^+WheelDown")
    zoom /= 1.189207115
  Zx := Rx / zoom
  Zy := Ry / zoom
}

#NoEnv
SetBatchLines -1

CoordMode Mouse, Screen
OnExit GuiClose
zoom = 2                ; initial magnification, 1..32
antialize = 0
Rx = 128                ; half vertical/horizontal side of magnifier window
Ry = 128
Zx := Rx/zoom           ; frame x/y size
Zy := Ry/zoom
                        ; GUI to show the magnified image
Gui +AlwaysOnTop +Resize
Gui Show, % "w" 4*Rx " h" 4*Ry " x0 y0", Magnifier
WinGet MagnifierID, id,  Magnifier
WinSet Transparent, 255, Magnifier ; makes the window invisible to magnification
WinGet PrintSourceID, ID 


hdd_frame := DllCall("GetDC", UInt, PrintSourceID)
hdc_frame := DllCall("GetDC", UInt, MagnifierID)

MsgBox ,  \%PrintSourceID%\%MagnifierID%

SetTimer Repaint, 50    ; flow through

Repaint:
   MouseGetPos x, y
   xz := In(x-Zx-6,0,A_ScreenWidth-2*Zx) ; keep the frame on screen
   yz := In(y-Zy-6,0,A_ScreenHeight-2*Zy)
  ; WinMove Frame,,%xz%, %yz%, % 2*Zx, % 2*Zy
   DllCall("gdi32.dll\StretchBlt", UInt,hdc_frame, Int,0, Int,0, Int,2*Rx, Int,2*Ry
   , UInt,hdd_frame, UInt,xz, UInt,yz, Int,2*Zx, Int,2*Zy, UInt,0xCC0020) ; SRCCOPY
Return


GuiSize:
   Rx := A_GuiWidth/2
   Ry := A_GuiHeight/2
   Zx := Rx/zoom
   Zy := Ry/zoom
   ; TrayTip,,% "Frame  =  " Round(2*Zx) " × " Round(2*Zy) "`nMagnified to = " A_GuiWidth "×" A_GuiHeight
Return

#a::
  antialize := !antialize
  DllCall( "gdi32.dll\SetStretchBltMode", "uint", hdc_frame, "int", 4*antialize )  ; Antializing ?
Return 

#x::
GuiClose:
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdd_frame )
ExitApp

#p::
MButton::
   if paused = 
   {
        Gui, 2:Hide 
        Gui, Hide 
        SetTimer, Repaint, Off
        paused = 1
   }
   else
   {
        Gui, 2:Show 
        Gui, Show 
        SetTimer, Repaint, 50
        paused =
   }
Return

^+Up::
^+Down::
^+WheelUp::                      ; Ctrl+Shift+WheelUp to zoom in
^+WheelDown::                    ; Ctrl+Shift+WheelUp to zoom out
   If (zoom < 31 and ( A_ThisHotKey = "^+WheelUp" or A_ThisHotKey = "^+Up" ))
      zoom *= 1.189207115         ; sqrt(sqrt(2))
   If (zoom >  1 and ( A_ThisHotKey = "^+WheelDown" or A_ThisHotKey = "^+Down" ))
      zoom /= 1.189207115
   Zx := Rx/zoom
   Zy := Ry/zoom
   ; TrayTip,,% "Zoom = " Round(100*zoom) "%"
Return

In(x,a,b) {                      ; closest number to x in [a,b]
   IfLess x,%a%, Return a
   IfLess b,%x%, Return b
   Return x
}