You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
clipboard/readhandle_windows.go

217 lines
5.7 KiB
Go

package clipboard
import (
"b612.me/win32api"
"bytes"
"encoding/binary"
"errors"
"fmt"
"golang.org/x/image/bmp"
"image"
"image/color"
"image/png"
"reflect"
"strings"
"syscall"
"unsafe"
)
func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size uint32) ([]byte, error)) ([]byte, error) {
mem, err := win32api.GetClipboardData(uFormat)
if err != nil {
return nil, fmt.Errorf("GetClipboardData failed: %v", err)
}
p, err := win32api.GlobalLock(mem)
if err != nil {
return nil, fmt.Errorf("GlobalLock failed: %v", err)
}
defer win32api.GlobalUnlock(mem)
size, err := win32api.GlobalSize(mem)
if err != nil {
return nil, fmt.Errorf("GlobalSize failed: %v", err)
}
if fn == nil {
return defaultFetchFn(p, uint32(size))
}
return fn(p, uint32(size))
}
func defaultFetchFn(p unsafe.Pointer, size uint32) ([]byte, error) {
var buf []byte
for i := 0; i < int(size); i++ {
buf = append(buf, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(i))))
}
return buf, nil
}
func AutoFetcher(uFormat string) ([]byte, error) {
switch uFormat {
case "CF_TEXT", "CF_UNICODETEXT":
return fetchClipboardData(win32api.CF_UNICODETEXT, textFetcher)
case "HTML Format":
return fetchClipboardData(win32api.RegisterClipboardFormat("HTML Format"), nil)
case "CF_HDROP":
return fetchClipboardData(win32api.CF_HDROP, filedropFetcher)
case "CF_DIBV5":
return fetchClipboardData(win32api.CF_DIBV5, cfDIBv5Fetcher)
case "CF_DIB":
return fetchClipboardData(win32api.CF_DIB, cfDIBFetcher)
case "PNG":
return fetchClipboardData(win32api.RegisterClipboardFormat("PNG"), nil)
}
return nil, errors.New("not support uFormat:" + uFormat)
}
func textFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
var buf []uint16
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*((*uint16)(unsafe.Pointer(p))))) {
buf = append(buf, *(*uint16)(ptr))
}
return []byte(syscall.UTF16ToString(buf)), nil
}
func filedropFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
var res []string
c, err := win32api.DragQueryFile(win32api.HDROP(p), 0xFFFFFFFF, nil, 0)
if err != nil {
return nil, err
}
count := int(c)
for i := 0; i < count; i++ {
c, err = win32api.DragQueryFile(win32api.HDROP(p), win32api.DWORD(i), nil, 0)
if err != nil {
return nil, err
}
length := int(c)
buffer := make([]uint16, length+1)
_, err = win32api.DragQueryFile(win32api.HDROP(p), win32api.DWORD(i),
&buffer[0], win32api.DWORD(len(buffer)))
res = append(res, syscall.UTF16ToString(buffer))
}
return []byte(strings.Join(res, "|")), nil
}
func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
// inspect header information
info := (*bitmapV5Header)(unsafe.Pointer(p))
// maybe deal with other formats?
if info.BitCount != 32 {
return nil, errors.New("not support image format")
}
var data []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
sh.Data = uintptr(p)
sh.Cap = int(info.Size + 4*uint32(info.Width)*uint32(info.Height))
sh.Len = int(info.Size + 4*uint32(info.Width)*uint32(info.Height))
img := image.NewRGBA(image.Rect(0, 0, int(info.Width), int(info.Height)))
offset := int(info.Size)
stride := int(info.Width)
for y := 0; y < int(info.Height); y++ {
for x := 0; x < int(info.Width); x++ {
idx := offset + 4*(y*stride+x)
xhat := (x + int(info.Width)) % int(info.Width)
yhat := int(info.Height) - 1 - y
r := data[idx+2]
g := data[idx+1]
b := data[idx+0]
a := data[idx+3]
img.SetRGBA(xhat, yhat, color.RGBA{r, g, b, a})
}
}
// always use PNG encoding.
var buf bytes.Buffer
err := png.Encode(&buf, img)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func cfDIBFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
// 函数意外报错,待修正
const (
fileHeaderLen = 14
infoHeaderLen = 40
)
bmpHeader := (*bitmapHeader)(p)
dataSize := bmpHeader.SizeImage + fileHeaderLen + infoHeaderLen
if bmpHeader.SizeImage == 0 && bmpHeader.Compression == 0 {
iSizeImage := bmpHeader.Height * ((bmpHeader.Width*uint32(bmpHeader.BitCount)/8 + 3) &^ 3)
dataSize += iSizeImage
}
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint16('B')|(uint16('M')<<8))
binary.Write(buf, binary.LittleEndian, uint32(dataSize))
binary.Write(buf, binary.LittleEndian, uint32(0))
const sizeof_colorbar = 0
binary.Write(buf, binary.LittleEndian, uint32(fileHeaderLen+infoHeaderLen+sizeof_colorbar))
j := 0
for i := fileHeaderLen; i < int(dataSize); i++ {
binary.Write(buf, binary.BigEndian, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(j))))
j++
}
return bmpToPng(buf)
}
func bmpToPng(bmpBuf *bytes.Buffer) (buf []byte, err error) {
var f bytes.Buffer
original_image, err := bmp.Decode(bmpBuf)
if err != nil {
return nil, err
}
err = png.Encode(&f, original_image)
if err != nil {
return nil, err
}
return f.Bytes(), nil
}
type bitmapV5Header struct {
Size uint32
Width int32
Height int32
Planes uint16
BitCount uint16
Compression uint32
SizeImage uint32
XPelsPerMeter int32
YPelsPerMeter int32
ClrUsed uint32
ClrImportant uint32
RedMask uint32
GreenMask uint32
BlueMask uint32
AlphaMask uint32
CSType uint32
Endpoints struct {
CiexyzRed, CiexyzGreen, CiexyzBlue struct {
CiexyzX, CiexyzY, CiexyzZ int32 // FXPT2DOT30
}
}
GammaRed uint32
GammaGreen uint32
GammaBlue uint32
Intent uint32
ProfileData uint32
ProfileSize uint32
Reserved uint32
}
type bitmapHeader struct {
Size uint32
Width uint32
Height uint32
PLanes uint16
BitCount uint16
Compression uint32
SizeImage uint32
XPelsPerMeter uint32
YPelsPerMeter uint32
ClrUsed uint32
ClrImportant uint32
}