init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib

This commit is contained in:
w11
2026-09-19 18:55:43 +08:00
commit 1096fbce9d
94 changed files with 36790 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
// image_dib.go 将 Windows 剪贴板的 CF_DIB(BITMAPINFO + 像素)转换为 PNG。
//
// 剪贴板截图类内容常见两种来源:现代应用直接提供 "PNG" 注册格式;老应用
// (如 Server 上的画图)只提供 CF_DIB。本转换器覆盖最常见子集:
// BI_RGB / BI_BITFIELDS 压缩、24/32bpp、底行优先(默认)与 top-down。
package cliprdr
import (
"bytes"
"encoding/binary"
"fmt"
"image"
"image/png"
)
// DIBToPNG 把 CF_DIB 数据解码并编码为 PNG;不支持的子集返回错误。
func DIBToPNG(dib []byte) ([]byte, error) {
if len(dib) < 40 {
return nil, fmt.Errorf("dib too short: %d", len(dib))
}
biSize := binary.LittleEndian.Uint32(dib[0:4])
w := int(int32(binary.LittleEndian.Uint32(dib[4:8])))
hRaw := int32(binary.LittleEndian.Uint32(dib[8:12]))
topDown := hRaw < 0
h := int(hRaw)
if h < 0 {
h = -h
}
bpp := binary.LittleEndian.Uint16(dib[14:16])
compression := binary.LittleEndian.Uint32(dib[16:20])
if w <= 0 || h <= 0 || w > 16384 || h > 16384 {
return nil, fmt.Errorf("bad dimensions %dx%d", w, h)
}
var rMask, gMask, bMask uint32
pixOff := int(biSize)
switch compression {
case 0: // BI_RGB
rMask, gMask, bMask = 0x00FF0000, 0x0000FF00, 0x000000FF
case 3: // BI_BITFIELDS
if biSize >= 108 { // BITMAPV4HEADER/V5HEADER 内含掩码
rMask = binary.LittleEndian.Uint32(dib[40:44])
gMask = binary.LittleEndian.Uint32(dib[44:48])
bMask = binary.LittleEndian.Uint32(dib[48:52])
} else { // 三个 DWORD 掩码紧跟标准头
if pixOff+12 > len(dib) {
return nil, fmt.Errorf("missing bitfields masks")
}
rMask = binary.LittleEndian.Uint32(dib[pixOff:])
gMask = binary.LittleEndian.Uint32(dib[pixOff+4:])
bMask = binary.LittleEndian.Uint32(dib[pixOff+8:])
pixOff += 12
}
default:
return nil, fmt.Errorf("unsupported compression %d", compression)
}
if bpp != 24 && bpp != 32 {
return nil, fmt.Errorf("unsupported bpp %d", bpp)
}
stride := (w*int(bpp) + 31) / 32 * 4
if pixOff+stride*h > len(dib) {
return nil, fmt.Errorf("dib truncated: need %d, have %d", pixOff+stride*h, len(dib))
}
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
srcY := y
if !topDown {
srcY = h - 1 - y
}
row := dib[pixOff+srcY*stride:]
for x := 0; x < w; x++ {
var r8, g8, b8 uint8
if bpp == 32 {
v := binary.LittleEndian.Uint32(row[x*4:])
r8, g8, b8 = maskTo8(v, rMask), maskTo8(v, gMask), maskTo8(v, bMask)
} else {
o := x * 3
b8, g8, r8 = row[o], row[o+1], row[o+2]
}
i := img.PixOffset(x, y)
img.Pix[i], img.Pix[i+1], img.Pix[i+2], img.Pix[i+3] = r8, g8, b8, 255
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// maskTo8 把 mask 提取出的位域缩放为 8 位
func maskTo8(v, mask uint32) uint8 {
if mask == 0 {
return 0
}
shift := 0
for mask&1 == 0 {
mask >>= 1
shift++
}
bits := 0
for mask != 0 {
mask >>= 1
bits++
}
val := (v >> uint(shift)) & ((1 << uint(bits)) - 1)
if bits < 8 {
return uint8(val<<(8-bits) | val>>(2*uint(bits)-8))
}
return uint8(val >> uint(bits-8))
}