dt_automate/tool/jietu.go

260 lines
6.8 KiB
Go
Raw Normal View History

2025-02-19 18:30:19 +08:00
package tool
import (
"image"
"image/png"
"log"
"os"
"strings"
"syscall"
"unsafe"
"github.com/kbinani/screenshot"
)
// Windows API常量
const (
SRCCOPY = 0x00CC0020 // 复制操作的代码
)
// Windows API结构体定义
type RECT struct { // 矩形区域结构
Left, Top, Right, Bottom int32
}
type BITMAPINFOHEADER struct { // 位图信息头
Size uint32
Width int32
Height int32
Planes uint16
BitCount uint16
Compression uint32
SizeImage uint32
XPelsPerMeter int32
YPelsPerMeter int32
ClrUsed uint32
ClrImportant uint32
}
var (
user32 = syscall.NewLazyDLL("user32.dll")
procEnumWindows = user32.NewProc("EnumWindows")
procGetWindowText = user32.NewProc("GetWindowTextW")
titless string
)
// 定义回调函数的类型
type EnumWindowsProc func(hwnd uintptr, lParam uintptr) uintptr
// 枚举窗口的回调函数
func enumWindowsProc(hwnd uintptr, lParam uintptr) uintptr {
var buffer [256]uint16
length, _, _ := procGetWindowText.Call(hwnd, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)))
if length > 0 {
title := syscall.UTF16ToString(buffer[:length])
// fmt.Printf("Window Handle: %x, Title: %s\n", hwnd, title)
if strings.Contains(title, "Chromium") && title != "GFoxChromiumCloseForm" {
titless = title
log.Println(titless)
}
}
return 1 // 返回非零值以继续枚举
}
func Jietu(path_img string) {
// 将 Go 回调函数转换为 Windows 可调用的回调函数
cb := syscall.NewCallback(enumWindowsProc)
// 调用 EnumWindows 函数
procEnumWindows.Call(cb, 0)
// 需要截图的窗口标题(根据实际情况修改)
windowTitle := titless
// 加载Windows DLL
user32 := syscall.NewLazyDLL("user32.dll")
// gdi32 := syscall.NewLazyDLL("gdi32.dll")
// 获取API函数指针
findWindow := user32.NewProc("FindWindowW")
getWindowDC := user32.NewProc("GetWindowDC")
getWindowRect := user32.NewProc("GetWindowRect")
releaseDC := user32.NewProc("ReleaseDC")
getDpiForWindow := user32.NewProc("GetDpiForWindow") // 获取窗口DPI
// createCompatibleDC := gdi32.NewProc("CreateCompatibleDC")
// createCompatibleBitmap := gdi32.NewProc("CreateCompatibleBitmap")
// selectObject := gdi32.NewProc("SelectObject")
// bitBlt := gdi32.NewProc("BitBlt")
// deleteDC := gdi32.NewProc("DeleteDC")
// deleteObject := gdi32.NewProc("DeleteObject")
// getDIBits := gdi32.NewProc("GetDIBits")
// 1. 查找目标窗口句柄
windowName := syscall.StringToUTF16Ptr(windowTitle)
hwnd, _, _ := findWindow.Call(0, uintptr(unsafe.Pointer(windowName)))
if hwnd == 0 {
panic("找不到指定窗口")
}
// 2. 获取窗口设备上下文DC
hdc, _, _ := getWindowDC.Call(hwnd)
if hdc == 0 {
panic("获取窗口DC失败")
}
defer releaseDC.Call(hwnd, hdc) // 确保释放DC
// 3. 获取窗口尺寸
var rect RECT
ret, _, _ := getWindowRect.Call(hwnd, uintptr(unsafe.Pointer(&rect)))
if ret == 0 {
panic("获取窗口尺寸失败")
}
// 4. 获取窗口DPI
dpi, _, _ := getDpiForWindow.Call(hwnd)
if dpi == 0 {
dpi = 96 // 默认DPI
}
// 5. 计算实际窗口尺寸考虑DPI缩放
scaleFactor := float64(dpi) / 216
width := int(float64(rect.Right-rect.Left) / scaleFactor)
height := int(float64(rect.Bottom-rect.Top) / scaleFactor)
log.Println(width, height)
// // 6. 创建内存兼容DC
// memHdc, _, _ := createCompatibleDC.Call(hdc)
// if memHdc == 0 {
// panic("创建兼容DC失败")
// }
// defer deleteDC.Call(memHdc)
// // 7. 创建兼容位图
// hBitmap, _, _ := createCompatibleBitmap.Call(hdc, uintptr(width), uintptr(height))
// if hBitmap == 0 {
// panic("创建兼容位图失败")
// }
// defer deleteObject.Call(hBitmap)
// // 8. 将位图选入内存DC
// oldObj, _, _ := selectObject.Call(memHdc, hBitmap)
// if oldObj == 0 {
// panic("选定位图失败")
// }
// defer selectObject.Call(memHdc, oldObj) // 恢复原始对象
// // 9. 执行位块传输(截图操作)
// ret, _, _ = bitBlt.Call(
// memHdc, // 目标DC
// 0, 0, // 目标坐标
// uintptr(width), // 宽度
// uintptr(height), // 高度
// hdc, // 源DC
// 0, 0, // 源坐标
// SRCCOPY, // 操作类型
// )
// if ret == 0 {
// panic("截图操作失败")
// }
// // 10. 准备位图信息头设置为top-down格式
// bmi := BITMAPINFOHEADER{
// Size: uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
// Width: int32(width),
// Height: -int32(height), // 负数表示top-down格式
// Planes: 1,
// BitCount: 32, // 32位色深BGRA
// Compression: 0, // 无压缩
// }
// // 11. 创建像素缓冲区
// pixels := make([]byte, width*height*4)
// // 12. 获取位图数据
// ret, _, _ = getDIBits.Call(
// memHdc,
// hBitmap,
// 0,
// uintptr(height),
// uintptr(unsafe.Pointer(&pixels[0])),
// uintptr(unsafe.Pointer(&bmi)),
// 0,
// )
// if ret == 0 {
// panic("获取像素数据失败")
// }
// // 13. 创建图像对象并转换颜色格式
// img := image.NewRGBA(image.Rect(0, 0, width, height))
// for y := 0; y < height; y++ {
// for x := 0; x < width; x++ {
// idx := (y*width + x) * 4
// // Windows返回BGRA格式转换为RGBA
// img.Set(x, y, color.RGBA{
// R: pixels[idx+2],
// G: pixels[idx+1],
// B: pixels[idx],
// A: pixels[idx+3],
// })
// }
// }
// // 14. 保存为PNG文件
// file, err := os.Create("window_screenshot.png")
// if err != nil {
// panic("创建文件失败: " + err.Error())
// }
// defer file.Close()
// if err := png.Encode(file, img); err != nil {
// panic("保存PNG失败: " + err.Error())
// }
//自定义截图
// img, err := screenshot.Capture(0, 0, width, height)
// if err != nil {
// panic(err)
// }
// save(img, "自定义.png")
//获取所有活动屏幕
// n := screenshot.NumActiveDisplays()
// if n <= 0 {
// panic("没有发现活动的显示器")
// }
// //全屏截取所有活动屏幕
// if n > 0 {
// for i := 0; i < n; i++ {
// img, err := screenshot.CaptureDisplay(i)
// if err != nil {
// panic(err)
// }
// fileName := fmt.Sprintf("第%d屏幕截图.png", i)
// save(img, fileName)
// }
// }
// 获取第一个屏幕显示范围
var new image.Rectangle = image.Rect(22, 15, width+5, height+3)
img, err := screenshot.CaptureRect(new)
if err != nil {
panic(err)
}
save(img, path_img)
// //使用Union获取指定屏幕 矩形最小点和最大点
// var all image.Rectangle = image.Rect(0, 0, 0, 0)
// bounds1 := screenshot.GetDisplayBounds(0)
// all = bounds1.Union(all)
// fmt.Println(all.Min.X, all.Min.Y, all.Dx(), all.Dy())
}
// save *image.RGBA to filePath with PNG format.
func save(img *image.RGBA, filePath string) {
file, err := os.Create(filePath)
if err != nil {
panic(err)
}
defer file.Close()
png.Encode(file, img)
}