mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
299 lines
6.1 KiB
Go
299 lines
6.1 KiB
Go
package shm
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
type PixelFormat uint32
|
|
|
|
const (
|
|
FormatARGB8888 PixelFormat = 0
|
|
FormatXRGB8888 PixelFormat = 1
|
|
FormatABGR8888 PixelFormat = 0x34324241
|
|
FormatXBGR8888 PixelFormat = 0x34324258
|
|
FormatRGB888 PixelFormat = 0x34324752
|
|
FormatBGR888 PixelFormat = 0x34324742
|
|
)
|
|
|
|
func (f PixelFormat) BytesPerPixel() int {
|
|
switch f {
|
|
case FormatRGB888, FormatBGR888:
|
|
return 3
|
|
default:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
func (f PixelFormat) Is24Bit() bool {
|
|
return f == FormatRGB888 || f == FormatBGR888
|
|
}
|
|
|
|
type Buffer struct {
|
|
fd int
|
|
data []byte
|
|
size int
|
|
Width int
|
|
Height int
|
|
Stride int
|
|
Format PixelFormat
|
|
}
|
|
|
|
func CreateBuffer(width, height, stride int) (*Buffer, error) {
|
|
size := stride * height
|
|
|
|
fd, err := unix.MemfdCreate("dms-shm", 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("memfd_create: %w", err)
|
|
}
|
|
|
|
if err := unix.Ftruncate(fd, int64(size)); err != nil {
|
|
unix.Close(fd)
|
|
return nil, fmt.Errorf("ftruncate: %w", err)
|
|
}
|
|
|
|
data, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
|
if err != nil {
|
|
unix.Close(fd)
|
|
return nil, fmt.Errorf("mmap: %w", err)
|
|
}
|
|
|
|
return &Buffer{
|
|
fd: fd,
|
|
data: data,
|
|
size: size,
|
|
Width: width,
|
|
Height: height,
|
|
Stride: stride,
|
|
}, nil
|
|
}
|
|
|
|
func (b *Buffer) Fd() int { return b.fd }
|
|
func (b *Buffer) Size() int { return b.size }
|
|
func (b *Buffer) Data() []byte { return b.data }
|
|
|
|
func (b *Buffer) Close() error {
|
|
var firstErr error
|
|
|
|
if b.data != nil {
|
|
if err := unix.Munmap(b.data); err != nil && firstErr == nil {
|
|
firstErr = fmt.Errorf("munmap: %w", err)
|
|
}
|
|
b.data = nil
|
|
}
|
|
|
|
if b.fd >= 0 {
|
|
if err := unix.Close(b.fd); err != nil && firstErr == nil {
|
|
firstErr = fmt.Errorf("close: %w", err)
|
|
}
|
|
b.fd = -1
|
|
}
|
|
|
|
return firstErr
|
|
}
|
|
|
|
func (b *Buffer) ConvertTo32Bit(srcFormat PixelFormat) (*Buffer, PixelFormat, error) {
|
|
if !srcFormat.Is24Bit() {
|
|
return b, srcFormat, nil
|
|
}
|
|
|
|
dstFormat := FormatXRGB8888
|
|
dstStride := b.Width * 4
|
|
|
|
dst, err := CreateBuffer(b.Width, b.Height, dstStride)
|
|
if err != nil {
|
|
return nil, srcFormat, err
|
|
}
|
|
dst.Format = dstFormat
|
|
|
|
srcData := b.data
|
|
dstData := dst.data
|
|
|
|
// DRM format names are counterintuitive on little-endian:
|
|
// RGB888 memory layout: B, G, R (name is logical order, not memory)
|
|
// BGR888 memory layout: R, G, B
|
|
isBGRMemory := srcFormat == FormatRGB888
|
|
|
|
for y := 0; y < b.Height; y++ {
|
|
srcRow := y * b.Stride
|
|
dstRow := y * dstStride
|
|
for x := 0; x < b.Width; x++ {
|
|
si := srcRow + x*3
|
|
di := dstRow + x*4
|
|
if isBGRMemory {
|
|
// RGB888: src memory is B,G,R -> dst XRGB8888 memory B,G,R,X
|
|
dstData[di+0] = srcData[si+0]
|
|
dstData[di+1] = srcData[si+1]
|
|
dstData[di+2] = srcData[si+2]
|
|
} else {
|
|
// BGR888: src memory is R,G,B -> dst XRGB8888 memory B,G,R,X
|
|
dstData[di+0] = srcData[si+2]
|
|
dstData[di+1] = srcData[si+1]
|
|
dstData[di+2] = srcData[si+0]
|
|
}
|
|
dstData[di+3] = 0xFF
|
|
}
|
|
}
|
|
|
|
return dst, dstFormat, nil
|
|
}
|
|
|
|
func (b *Buffer) GetPixelRGBA(x, y int) (r, g, b2, a uint8) {
|
|
if x < 0 || x >= b.Width || y < 0 || y >= b.Height {
|
|
return
|
|
}
|
|
|
|
off := y*b.Stride + x*4
|
|
if off+3 >= len(b.data) {
|
|
return
|
|
}
|
|
|
|
switch b.Format {
|
|
case FormatXBGR8888, FormatABGR8888:
|
|
return b.data[off], b.data[off+1], b.data[off+2], 0xFF
|
|
default:
|
|
return b.data[off+2], b.data[off+1], b.data[off], 0xFF
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) GetPixelBGRA(x, y int) (b2, g, r, a uint8) {
|
|
if x < 0 || x >= b.Width || y < 0 || y >= b.Height {
|
|
return
|
|
}
|
|
|
|
off := y*b.Stride + x*4
|
|
if off+3 >= len(b.data) {
|
|
return
|
|
}
|
|
|
|
return b.data[off], b.data[off+1], b.data[off+2], b.data[off+3]
|
|
}
|
|
|
|
func (b *Buffer) ConvertBGRAtoRGBA() {
|
|
for y := 0; y < b.Height; y++ {
|
|
rowOff := y * b.Stride
|
|
for x := 0; x < b.Width; x++ {
|
|
off := rowOff + x*4
|
|
if off+3 >= len(b.data) {
|
|
continue
|
|
}
|
|
b.data[off], b.data[off+2] = b.data[off+2], b.data[off]
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) FlipVertical() {
|
|
tmp := make([]byte, b.Stride)
|
|
for y := 0; y < b.Height/2; y++ {
|
|
topOff := y * b.Stride
|
|
botOff := (b.Height - 1 - y) * b.Stride
|
|
copy(tmp, b.data[topOff:topOff+b.Stride])
|
|
copy(b.data[topOff:topOff+b.Stride], b.data[botOff:botOff+b.Stride])
|
|
copy(b.data[botOff:botOff+b.Stride], tmp)
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) Clear() {
|
|
for i := range b.data {
|
|
b.data[i] = 0
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) CopyFrom(src *Buffer) {
|
|
copy(b.data, src.data)
|
|
}
|
|
|
|
const (
|
|
TransformNormal = 0
|
|
Transform90 = 1
|
|
Transform180 = 2
|
|
Transform270 = 3
|
|
TransformFlipped = 4
|
|
TransformFlipped90 = 5
|
|
TransformFlipped180 = 6
|
|
TransformFlipped270 = 7
|
|
)
|
|
|
|
func (b *Buffer) ApplyTransform(transform int32) (*Buffer, error) {
|
|
if transform == TransformNormal {
|
|
return b, nil
|
|
}
|
|
|
|
var newW, newH int
|
|
switch transform {
|
|
case Transform90, Transform270, TransformFlipped90, TransformFlipped270:
|
|
newW, newH = b.Height, b.Width
|
|
default:
|
|
newW, newH = b.Width, b.Height
|
|
}
|
|
|
|
newBuf, err := CreateBuffer(newW, newH, newW*4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newBuf.Format = b.Format
|
|
|
|
srcData := b.data
|
|
dstData := newBuf.data
|
|
|
|
for sy := 0; sy < b.Height; sy++ {
|
|
for sx := 0; sx < b.Width; sx++ {
|
|
var dx, dy int
|
|
|
|
switch transform {
|
|
case Transform90: // 90° CCW
|
|
dx = sy
|
|
dy = b.Width - 1 - sx
|
|
case Transform180:
|
|
dx = b.Width - 1 - sx
|
|
dy = b.Height - 1 - sy
|
|
case Transform270: // 270° CCW = 90° CW
|
|
dx = b.Height - 1 - sy
|
|
dy = sx
|
|
case TransformFlipped:
|
|
dx = b.Width - 1 - sx
|
|
dy = sy
|
|
case TransformFlipped90:
|
|
dx = sy
|
|
dy = sx
|
|
case TransformFlipped180:
|
|
dx = sx
|
|
dy = b.Height - 1 - sy
|
|
case TransformFlipped270:
|
|
dx = b.Height - 1 - sy
|
|
dy = b.Width - 1 - sx
|
|
default:
|
|
dx, dy = sx, sy
|
|
}
|
|
|
|
si := sy*b.Stride + sx*4
|
|
di := dy*newBuf.Stride + dx*4
|
|
|
|
if si+3 < len(srcData) && di+3 < len(dstData) {
|
|
dstData[di+0] = srcData[si+0]
|
|
dstData[di+1] = srcData[si+1]
|
|
dstData[di+2] = srcData[si+2]
|
|
dstData[di+3] = srcData[si+3]
|
|
}
|
|
}
|
|
}
|
|
|
|
return newBuf, nil
|
|
}
|
|
|
|
func InverseTransform(transform int32) int32 {
|
|
switch transform {
|
|
case Transform90:
|
|
return Transform270
|
|
case Transform270:
|
|
return Transform90
|
|
case TransformFlipped90:
|
|
return TransformFlipped270
|
|
case TransformFlipped270:
|
|
return TransformFlipped90
|
|
default:
|
|
return transform
|
|
}
|
|
}
|