init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib
This commit is contained in:
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user