feat: Initial commit

This commit is contained in:
ptrcnull 2021-12-04 07:08:00 +01:00
commit e260768039
7 changed files with 254 additions and 0 deletions

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# imagedrm
A simple library that wraps [godrm](https://github.com/kytart/godrm) as [draw.Image](https://pkg.go.dev/image/draw#Image)
See example usage in `examples/`.

145
drm.go Normal file
View File

@ -0,0 +1,145 @@
package imagedrm
import (
"fmt"
"github.com/kytart/godrm/pkg/drm"
"github.com/kytart/godrm/pkg/mode"
"launchpad.net/gommap"
"os"
)
type Framebuffer struct {
*mode.FB
id uint32
data []byte
}
type Image struct {
file *os.File
modeset *mode.SimpleModeset
displays []*Display
}
type Display struct {
mode *mode.Modeset
fb *Framebuffer
savedCrtc *mode.Crtc
}
func NewImage() (*Image, error) {
file, err := drm.OpenCard(0)
if err != nil {
return nil, fmt.Errorf("open drm card: %w", err)
}
defer file.Close()
if !drm.HasDumbBuffer(file) {
return nil, fmt.Errorf("drm device does not support dumb buffers")
}
modeset, err := mode.NewSimpleModeset(file)
if err != nil {
return nil, fmt.Errorf("create modeset: %w", err)
}
image := &Image{
file: file,
modeset: modeset,
}
for _, mod := range modeset.Modesets {
display, err := image.setupDisplay(mod)
if err != nil {
image.Close()
return nil, fmt.Errorf("setup display: %w", err)
}
image.displays = append(image.displays, display)
}
return image, nil
}
func (i *Image) Close() error {
var err error
for _, display := range i.displays {
err = i.destroyFramebuffer(display)
}
return err
}
func (i *Image) createFramebuffer(dev *mode.Modeset) (*Framebuffer, error) {
fb, err := mode.CreateFB(i.file, dev.Width, dev.Height, 32)
if err != nil {
return nil, fmt.Errorf("create framebuffer: %w", err)
}
fbID, err := mode.AddFB(i.file, dev.Width, dev.Height, 24, 32, fb.Pitch, fb.Handle)
if err != nil {
return nil, fmt.Errorf("create dumb buffer: %w", err)
}
offset, err := mode.MapDumb(i.file, fb.Handle)
if err != nil {
return nil, fmt.Errorf("map dumb: %w", err)
}
mmap, err := gommap.MapAt(0, i.file.Fd(), int64(offset), int64(fb.Size), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
if err != nil {
return nil, fmt.Errorf("mmap framebuffer: %w", err)
}
for i := uint64(0); i < fb.Size; i++ {
mmap[i] = 0
}
return &Framebuffer{
FB: fb,
id: fbID,
data: mmap,
}, nil
}
func (i *Image) destroyFramebuffer(display *Display) error {
err := gommap.MMap(display.fb.data).UnsafeUnmap()
if err != nil {
return fmt.Errorf("munmap memory: %w", err)
}
err = mode.RmFB(i.file, display.fb.id)
if err != nil {
return fmt.Errorf("remove frame buffer: %w", err)
}
err = mode.DestroyDumb(i.file, display.fb.Handle)
if err != nil {
return fmt.Errorf("destroy dumb buffer: %w", err)
}
return i.modeset.SetCrtc(display.mode, display.savedCrtc)
}
func (i *Image) setupDisplay(mod mode.Modeset) (*Display, error) {
framebuf, err := i.createFramebuffer(&mod)
if err != nil {
return nil, fmt.Errorf("create framebuffer: %w", err)
}
// save current CRTC of this display to restore at exit
savedCrtc, err := mode.GetCrtc(i.file, mod.Crtc)
if err != nil {
return nil, fmt.Errorf("get CRTC for connector %d: %w", mod.Conn, err)
}
// change the display
err = mode.SetCrtc(i.file, mod.Crtc, framebuf.id, 0, 0, &mod.Conn, 1, &mod.Mode)
if err != nil {
return nil, fmt.Errorf("set CRTC for connector %d: %w", mod.Conn, err)
}
return &Display{
mode: &mod,
fb: framebuf,
savedCrtc: savedCrtc,
}, nil
}

BIN
examples/jpeg/glenda.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

39
examples/jpeg/main.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"image"
"image/color"
"image/draw"
"os"
"time"
"github.com/ptrcnull/imagedrm"
)
func main() {
img, err := imagedrm.NewImage()
if err != nil {
panic(err)
}
defer img.Close()
sourceFile, err := os.Open("glenda.jpg")
if err != nil {
panic(err)
}
defer sourceFile.Close()
source, _, err := image.Decode(sourceFile)
if err != nil {
panic(err)
}
draw.Draw(img, source.Bounds(), source, image.Point{}, draw.Src)
for {
img.Set(100, 100, color.RGBA{R: 255, G: 255, B: 255})
time.Sleep(time.Second * 1)
img.Set(100, 100, color.RGBA{})
time.Sleep(time.Second * 1)
}
}

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module github.com/ptrcnull/imagedrm
go 1.17
require (
github.com/kytart/godrm v0.0.0-20210309160922-6b139ef54591
launchpad.net/gommap v0.0.0-20121012075617-000000000015
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
github.com/kytart/godrm v0.0.0-20210309160922-6b139ef54591 h1:FeNG18vTIhxL15MKaAZytDbspAPlVteebfedo5gabT0=
github.com/kytart/godrm v0.0.0-20210309160922-6b139ef54591/go.mod h1:Y+YdK8W4Pp8iogoLluZ0OenjBSNb9+8NqoNNEdYzxec=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
launchpad.net/gommap v0.0.0-20121012075617-000000000015 h1:ROjpWoAwfoub5UmgCuZwFvM/kWU+UFfuBXbJ6lz/awU=
launchpad.net/gommap v0.0.0-20121012075617-000000000015/go.mod h1:+rB9VlRTGlxdpc/nkCoOe0Qxgn/pyVwo9lGtVndZbOo=

51
image.go Normal file
View File

@ -0,0 +1,51 @@
package imagedrm
import (
"image"
"image/color"
"image/draw"
"unsafe"
)
func (i *Image) ColorModel() color.Model {
return &image.Uniform{}
}
func (i *Image) Bounds() image.Rectangle {
display := i.displays[0]
mode := display.mode
return image.Rectangle{
Min: image.Point{},
Max: image.Point{
X: int(mode.Width),
Y: int(mode.Height),
},
}
}
func (i *Image) At(x, y int) color.Color {
display := i.displays[0]
offset := (display.fb.Pitch * uint32(y)) + (uint32(x) * 4)
val := *(*uint32)(unsafe.Pointer(&display.fb.data[offset]))
return color.RGBA{
A: uint8((val & 0xff000000) >> 24),
R: uint8((val & 0x00ff0000) >> 16),
G: uint8((val & 0x0000ff00) >> 8),
B: uint8(val & 0x000000ff),
}
}
func (i *Image) Set(x, y int, c color.Color) {
display := i.displays[0]
r, g, b, a := c.RGBA()
val := (a << 24) | (r << 16) | (g << 8) | b
offset := (display.fb.Pitch * uint32(y)) + (uint32(x) * 4)
*(*uint32)(unsafe.Pointer(&display.fb.data[offset])) = val
}
var _ draw.Image = (*Image)(nil)