;  用法:截屏到剪贴板或者文件,可以加入开机脚本中热键调用
;
;  类似QQ微信的阴影截屏  By FeiYue


; 用手动调用截图,先选择范围然后保存到剪贴板或文件
F1:: SnapShot()


; 用命令行调用截图,文件参数为 0 或空就保存到剪贴板
F2::
file:="c:\1.bmp"
SnapShot( file, [0, 0, A_ScreenWidth, A_ScreenHeight] )
Run, % file
return




SnapShot(args*)
{
  return (new SnapShotClass).SnapShot(args*)
}

Class SnapShotClass
{  ;// 类开始

; 参  数: 文件名 和 范围数组 [x,y,w,h],都为空可以手动选择范围
; 返回值: 手动选择的范围数组 [x,y,w,h]

SnapShot(file:="", range:="")
{
  local

  ; 如果范围参数 range 是数组,就命令行调用
  if IsObject(range)
  {
    hBM:=this.BitmapFromScreen(range*)
    this.SaveBitmapToFile(file, hBM)
    DllCall("DeleteObject", "Ptr",hBM)
    return
  }

  ; 屏蔽热键的执行代码
  static Gui_Off:="", hk
  if (!Gui_Off)
    Gui_Off:=Func(A_ThisFunc).Bind(this, "<Off>")
  if (file="<Off>")
    return hk:=Trim(A_ThisHotkey, "*")

  ; 截屏到内存图像 hBM
  hBM:=this.BitmapFromScreen(zx:=0, zy:=0, zw:=0, zh:=0)

  ; 显示静态画布,兼做热键的条件
  Gui, SnapShot_HotkeyIf: New    ; WS_EX_NOACTIVATE:=0x08000000
  Gui, +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000
  Gui, Margin, 0, 0
  Gui, Add, Pic, w%zw% h%zh%, % "HBITMAP:*" hBM
  Gui, Show, NA x%zx% y%zy% w%zw% h%zh%, SnapShot_HotkeyIf
  this.DarkenRange(1)

  ; 生成保存按钮
  Gui, SnapShot_Box: New
  Gui, +Hwndbox_id +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000
  Gui, Margin, 0, 0
  Gui, Font, s12
  For k,v in StrSplit("重选|存文件|仅范围|存剪贴板", "|")
    Gui, Add, Button, % (k=1 ? "":"x+0") " Hwndid", %v%
  GuiControlGet, p, Pos, %id%
  box_w:=pX+pW+10, box_h:=pH+10
  Gui, Show, Hide, SnapShot_Box

  ; 设定屏蔽热键
  key:="LButton"
  KeyWait, RButton
  Hotkey, IfWinExist, SnapShot_HotkeyIf
  keys:=key "|Esc|Up|Down|Left|Right"
  For k,v in StrSplit(keys, "|")
  {
    KeyWait, %v%
    Hotkey, *%v%, %Gui_Off%, On UseErrorLevel
  }
  Hotkey, IfWinExist

  ; 开始选择范围
  Critical % (cri:=A_IsCritical)?"Off":"Off"
  CoordMode, Mouse
  Loop {  ;// 用于重选
  tip:="左键拖动选择范围,方向键微调`n右键或ESC仅范围,双击存剪贴板"
  hk:="", oldx:=oldy:="", ok:=0, d:=10, oldt:=0, oldf:=""
  Loop {
    Sleep 20
    if (hk="Esc") || GetKeyState("Esc","P") || GetKeyState("RButton","P")
    {
      x:=y:=w:=h:=0
      Break 2
    }
    MouseGetPos, x1, y1
    if (oldx=x1 && oldy=y1)
      Continue
    oldx:=x1, oldy:=y1
    ToolTip %tip%
  }
  Until (hk=key) || GetKeyState(key,"P")
  Loop {
    Sleep 20
    MouseGetPos, x2, y2
    ;-------------
    x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x1-x2)+1, h:=Abs(y1-y2)+1
    this.DarkenRange(x, y, w, h)
    ;-------------
    if (oldx=x2 && oldy=y2)
      Continue
    oldx:=x2, oldy:=y2
    ToolTip %tip%
  }
  Until !GetKeyState(key,"P")
  hk:=""
  Loop {
    Sleep 20
    MouseGetPos, x3, y3
    x1:=x, y1:=y, x2:=x+w-1, y2:=y+h-1
    , d1:=Abs(x3-x1)<=d, d2:=Abs(x3-x2)<=d
    , d3:=Abs(y3-y1)<=d, d4:=Abs(y3-y2)<=d
    , d5:=x3>x1+d && x3<x2-d, d6:=y3>y1+d && y3<y2-d
    , f:=(d1 && d3 ? 1 : d2 && d3 ? 2 : d1 && d4 ? 3
    : d2 && d4 ? 4 : d5 && d3 ? 5 : d5 && d4 ? 6
    : d6 && d1 ? 7 : d6 && d2 ? 8 : d5 && d6 ? 9 : 0)
    if (oldf!=f)
      oldf:=f, this.SetCursor(f=1 || f=4 ? "SIZENWSE"
      : f=2 || f=3 ? "SIZENESW" : f=5 || f=6 ? "SIZENS"
      : f=7 || f=8 ? "SIZEWE" : f=9 ? "SIZEALL" : "ARROW")
    ;--------------
    if (hk="Up") || GetKeyState("Up","P")
      hk:="", y--
    else if (hk="Down") || GetKeyState("Down","P")
      hk:="", y++
    else if (hk="Left") || GetKeyState("Left","P")
      hk:="", x--
    else if (hk="Right") || GetKeyState("Right","P")
      hk:="", x++
    else if (hk="Esc") || GetKeyState("Esc","P") || GetKeyState("RButton","P")
      Break
    else if (hk=key) || GetKeyState(key,"P")
    {
      MouseGetPos,,, id, class
      if (id=box_id) && (class="Button1")
      {
        KeyWait, % key
        Gui, SnapShot_Box: Hide
        this.DarkenRange(1)
        Continue 2
      }
      if (id=box_id) && (ok:=class="Button2" ? 2 : class="Button4" ? 1:100)
        Break
      Gui, SnapShot_Box: Hide
      ToolTip
      Loop {
        Sleep 20
        MouseGetPos, x4, y4
        x1:=x, y1:=y, x2:=x+w-1, y2:=y+h-1, dx:=x4-x3, dy:=y4-y3
        , (f=1 ? (x1+=dx, y1+=dy) : f=2 ? (x2+=dx, y1+=dy)
        : f=3 ? (x1+=dx, y2+=dy) : f=4 ? (x2+=dx, y2+=dy)
        : f=5 ? y1+=dy : f=6 ? y2+=dy : f=7 ? x1+=dx : f=8 ? x2+=dx
        : f=9 ? (x1+=dx, y1+=dy, x2+=dx, y2+=dy) : 0)
        , (f) && this.DarkenRange(Min(x1,x2), Min(y1,y2), Abs(x1-x2)+1, Abs(y1-y2)+1)
      }
      Until !GetKeyState(key,"P")
      hk:="", x:=Min(x1,x2), y:=Min(y1,y2), w:=Abs(x1-x2)+1, h:=Abs(y1-y2)+1
      if (f=9) && Abs(dx)<2 && Abs(dy)<2 && (ok:=(-oldt)+(oldt:=A_TickCount)<400)
        Break
    }
    this.DarkenRange(x, y, w, h)
    x1:=x+w-box_w, (x1<10 && x1:=10), (x1>zx+zw-box_w && x1:=zx+zw-box_w)
    , y1:=y+h+10, (y1>zy+zh-box_h && y1:=y-box_h), (y1<10 && y1:=10)
    Gui, SnapShot_Box: Show, NA x%x1% y%y1%
    ;-------------
    if (oldx=x3 && oldy=y3)
      Continue
    oldx:=x3, oldy:=y3
    ToolTip %tip%
  }
  Break
  }  ;// 用于重选
  this.SetCursor()
  ToolTip
  this.DarkenRange()
  Gui, SnapShot_Box: Destroy
  KeyWait, RButton
  Hotkey, IfWinExist, SnapShot_HotkeyIf
  For k,v in StrSplit(keys, "|")
  {
    KeyWait, %v%
    Hotkey, *%v%, %Gui_Off%, Off UseErrorLevel
  }
  Hotkey, IfWinExist
  Gui, SnapShot_HotkeyIf: Destroy
  Critical %cri%

  ; 保存内存图像到剪贴板或文件
  w:=Min(x+w,zx+zw), x:=Max(x,zx), w-=x
  h:=Min(y+h,zy+zh), y:=Max(y,zy), h-=y
  if (ok=1)
    this.SaveBitmapToFile(0, hBM, x-zx, y-zy, w, h)
  else if (ok=2)
  {
    FileSelectFile, file, S18, %A_Desktop%\1.bmp, 另存为, 图片 (*.bmp)
    this.SaveBitmapToFile(file, hBM, x-zx, y-zy, w, h)
  }

  ; 删除内存图像 hBM
  DllCall("DeleteObject", "Ptr",hBM)
  return [x, y, w, h]
}

BitmapFromScreen(ByRef x:=0, ByRef y:=0, ByRef w:=0, ByRef h:=0)
{
  local
  x:=Floor(x), y:=Floor(y), w:=Floor(w), h:=Floor(h)
  if (w<1 || h<1)
  {
    SysGet, x, 76
    SysGet, y, 77
    SysGet, w, 78
    SysGet, h, 79
  }
  hBM:=this.CreateDIBSection(w, h, 32)
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  oBM:=DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM, "Ptr")
  ;-------------
  hDC:=DllCall("GetWindowDC", "Ptr",win:=DllCall("GetDesktopWindow", "Ptr"), "Ptr")
  DllCall("BitBlt", "Ptr",mDC, "int",0, "int",0, "int",w, "int",h
    , "Ptr",hDC, "int",x, "int",y, "uint",0xCC0020|0x40000000)
  DllCall("ReleaseDC", "Ptr",win, "Ptr",hDC)
  ;-------------
  DllCall("SelectObject", "Ptr",mDC, "Ptr",oBM)
  DllCall("DeleteDC", "Ptr",mDC)
  return hBM
}

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

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

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

DrawHBM(hBM, lines)
{
  local
  mDC:=DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  DllCall("SaveDC", "Ptr",mDC)
  DllCall("SelectObject", "Ptr",mDC, "Ptr",hBM)
  oldc:=""
  For k,v in lines
  if IsObject(v)
  {
    if (oldc!=v[5])
    {
      c:=((c:=v[5])&0xFF)<<16|c&0xFF00|(c>>16)&0xFF
      pen:=DllCall("CreatePen", "Int",0, "Int",1, "UInt",c, "Ptr")
      pen:=DllCall("SelectObject", "Ptr",mDC, "Ptr",pen)
      (oldc!="") && DllCall("DeleteObject", "Ptr",pen)
      brush:=DllCall("CreateSolidBrush", "UInt", c, "Ptr")
      brush:=DllCall("SelectObject", "Ptr",mDC, "Ptr",brush)
      (oldc!="") && DllCall("DeleteObject", "Ptr",brush)
      oldc:=v[5]
    }
    DllCall("Rectangle", "Ptr",mDC, "Int",v[1], "Int",v[2], "Int",v[1]+v[3], "Int",v[2]+v[4])
  }
  DllCall("RestoreDC", "Ptr",mDC, "Int",-1)
  DllCall("DeleteObject", "Ptr",mDC)
}

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

SetCursor(cursor:="")
{
  local
  static init:="", tab
  if (!init)
  {
    init:=1, tab:=[], ID:=Object("ARROW",32512, "SIZENWSE",32642
    , "SIZENESW",32643, "SIZEWE",32644, "SIZENS",32645, "SIZEALL",32646)
    , OnExit(Func(A_ThisFunc).Bind(this,"")), this.SetCursor()
    For k,v in ID
      tab[k]:=DllCall("CopyImage", "Ptr", DllCall("LoadCursor"
      , "Ptr",0, "Ptr",v, "Ptr"), "int",2, "int",0, "int",0, "int",0, "Ptr")
  }
  if (cursor!="") && tab.HasKey(cursor)
    DllCall("SetSystemCursor", "Ptr", DllCall("CopyImage", "Ptr",tab[cursor]
    , "int",2, "int",0, "int",0, "int",0, "Ptr"), "int",32512)
  else
    DllCall("SystemParametersInfo", "int",0x57, "int",0, "Ptr",0, "int",0)
}

DarkenRange(x:=0, y:=0, w:=0, h:=0)
{
  local
  static id:="", hBM1, hBM2, hBM3, zx, zy, zw, zh, oldx, oldy, oldw, oldh
  if (x=0 && y=0 && w=0 && h=0)
  {
    if (!id)
      return
    id:=""
    Gui, DarkenRange: Destroy
    DllCall("DeleteObject", "Ptr",hBM1)
    DllCall("DeleteObject", "Ptr",hBM2)
    DllCall("DeleteObject", "Ptr",hBM3)
    return
  }
  Gui, DarkenRange: +LastFoundExist
  IfWinNotExist
  {
    oldx:=oldy:=oldw:=oldh:=0
    hBM1:=this.BitmapFromScreen(zx:=0, zy:=0, zw:=0, zh:=0)
    ;------------
    Gui, DarkenRange: New
    Gui, +Hwndid +LastFound +AlwaysOnTop -Caption +ToolWindow -DPIScale
    Gui, Color, Black
    WinSet, Transparent, 100
    Gui, Show, NA x%zx% y%zy% w%zw% h%zh%
    Sleep 100
    ;------------
    hBM2:=this.BitmapFromScreen(zx,zy,zw,zh)
    hBM3:=DllCall("CopyImage","Ptr",hBM2,"int",0,"int",0,"int",0,"uint",0x2000,"Ptr")
    this.BitmapToWindow(id,0,0,hBM3,0,0,zw,zh)
    WinSet, Transparent, 255
  }
  if (oldx=x && oldy=y && oldw=w && oldh=h)
    return 1
  oldx:=x, oldy:=y, oldw:=w, oldh:=h
  x-=zx, y-=zy, d:=2, i:=1, j:=2*d+i, c:=0x3399FF
  lines:=[ 0
    , [x-i, y-i,    w+i*2, i, c]  ; 上横线
    , [x-i, y+h,    w+i*2, i, c]  ; 下横线
    , [x-i, y-i,    i, h+i*2, c]  ; 左竖线
    , [x+w, y-i,    i, h+i*2, c]  ; 右竖线
    , [x-i-d, y-i-d,    j, j, c]  ; 上左点
    , [x+w//2-d, y-i-d, j, j, c]  ; 上中点
    , [x+w-d, y-i-d,    j, j, c]  ; 上右点
    , [x-i-d, y+h//2-d, j, j, c]  ; 中左点
    , [x+w-d, y+h//2-d, j, j, c]  ; 中右点
    , [x-i-d, y+h-d,    j, j, c]  ; 下左点
    , [x+w//2-d, y+h-d, j, j, c]  ; 下中点
    , [x+w-d, y+h-d,    j, j, c]  ; 下右点
    , 0 ]
  this.CopyHBM(hBM3,0,0,hBM2,0,0,zw,zh)
  this.CopyHBM(hBM3,x,y,hBM1,x,y,w,h)
  this.DrawHBM(hBM3, lines)
  this.BitmapToWindow(id,0,0,hBM3,0,0,zw,zh)
  return 1
}

; if file = 0 or "", Save to Clipboard

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

}  ;// 类结束

 

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