From 169b79d0cbe2e82dbe1dba844c4d8195f2aa8f99 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 10 Dec 2019 04:49:40 +0000 Subject: [PATCH] move to self repo --- framebuffer.go | 166 ++++++++++++++++++++++++++++++++++++++++++++ framebuffer_test.go | 132 +++++++++++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 framebuffer.go create mode 100644 framebuffer_test.go diff --git a/framebuffer.go b/framebuffer.go new file mode 100644 index 0000000..b0e53ef --- /dev/null +++ b/framebuffer.go @@ -0,0 +1,166 @@ +package gofb + +/* +#include +#include +#include +#include +#include +#include + +int OpenFrameBuffer(char * name) { + return open(name, O_RDWR); +} + +static int GetFixedScreenInformation(int fd,struct fb_fix_screeninfo *finfo) +{ + + return ioctl(fd, FBIOGET_FSCREENINFO, finfo); +} + + +static int GetVarScreenInformation(int fd,struct fb_var_screeninfo *vinfo) { + return ioctl(fd, FBIOGET_VSCREENINFO, vinfo); +} +*/ +import "C" +import ( + "errors" + "fmt" + "image" + "os/exec" + "unsafe" +) + +type Framebuffer struct { + Fd int + BitsPerPixel int + Xres int + Yres int + Data []byte + Xoffset int + Yoffset int + LineLength int + Screensize int +} + +func NewFramebuffer() *Framebuffer { + return &Framebuffer{} +} + +func _HideCursor() { + fmt.Print("\033[?25l") + exec.Command(`kill `) +} + +func _ShowCursor() { + fmt.Printf("\033[?25h") +} + +func (f *Framebuffer) Init() { + _HideCursor() + dev_file := C.CString("/dev/fb0") + fd, err := C.OpenFrameBuffer(dev_file) + C.free(unsafe.Pointer(dev_file)) + + if err != nil { + panic(errors.New("Open the framebuffer failed")) + } + + var finfo C.struct_fb_fix_screeninfo + if _, err := C.GetFixedScreenInformation(fd, &finfo); err != nil { + fmt.Println(err) + } + + var vinfo C.struct_fb_var_screeninfo + if _, err := C.GetVarScreenInformation(fd, &vinfo); err != nil { + fmt.Println(err) + } + + f.Xres = int(vinfo.xres) + f.Yres = int(vinfo.yres) + f.BitsPerPixel = int(vinfo.bits_per_pixel) + f.Xoffset = int(vinfo.xoffset) + f.Yoffset = int(vinfo.yoffset) + f.LineLength = int(finfo.line_length) + + f.Screensize = int(finfo.smem_len) + + addr := uintptr(C.mmap(nil, C.size_t(f.Screensize), C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED, fd, 0)) + + var sl = struct { + addr uintptr + len int + cap int + }{addr, f.Screensize, f.Screensize} + + f.Data = *(*[]byte)(unsafe.Pointer(&sl)) + +} + +func (f *Framebuffer) Release() { + C.munmap(unsafe.Pointer(&f.Data[0]), C.size_t(f.Screensize)) + C.close(C.int(f.Fd)) + _ShowCursor() +} + +func (f *Framebuffer) SetPixel(x int, y int, r uint32, g uint32, b uint32, a uint32) { + if x < 0 || x > f.Xres { + panic(errors.New("X is too big or is negative")) + } + + if y < 0 || y > f.Yres { + panic(errors.New("Y is too big or is negative")) + } + + location := (x+f.Xoffset)*(f.BitsPerPixel/8) + (y+f.Yoffset)*f.LineLength + + f.Data[location+3] = byte(a & 0xff) + f.Data[location+2] = byte(r & 0xff) + f.Data[location+1] = byte(g & 0xff) + f.Data[location] = byte(b & 0xff) +} + +func (f *Framebuffer) DrawImage(xoffset int, yoffset int, image image.Image) { + + b := image.Bounds() + + for y := 0; y < b.Max.Y; y++ { + for x := 0; x < b.Max.X; x++ { + r, g, b, a := image.At(x, y).RGBA() + f.SetPixel(x+xoffset, y+yoffset, r&0xff, g&0xff, b&0xff, a&0xff) + } + } +} + +func (f *Framebuffer) DrawData(xoffset int, yoffset int, data []byte, w int, h int) { + + if w > f.Xres { + panic(errors.New("The width of data must NOT be bigger the Xres of the framebuffer")) + } + + for y := 0; y < h; y++ { + if (y+1)*w > len(data) { + panic(errors.New("The length of image data is too small or w(h) argument is wrong")) + } + + line_start := (xoffset+f.Xoffset)*(f.BitsPerPixel/8) + (y+yoffset+f.Yoffset)*f.LineLength + line_end := (xoffset+f.Xoffset)*(f.BitsPerPixel/8) + (y+1+yoffset+f.Yoffset)*f.LineLength - 1 + + if line_start+w > line_end { + panic(errors.New("The lines is too long beyond the framebuffer")) + } + + data_line := data[y*w*4 : (y+1)*w*4] + copy(f.Data[line_start:line_start+w*4], data_line) + } + +} + +func (f *Framebuffer) Fill(r, g, b, a uint32) { + for y := 0; y < f.Yres; y++ { + for x := 0; x < f.Xres; x++ { + f.SetPixel(x, y, r, g, b, a) + } + } +} diff --git a/framebuffer_test.go b/framebuffer_test.go new file mode 100644 index 0000000..35cb8c2 --- /dev/null +++ b/framebuffer_test.go @@ -0,0 +1,132 @@ +package gofb + +import ( + "image" + "testing" + + "github.com/ungerik/go-cairo" +) + +func BenchmarkFill(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + for i := 0; i < b.N; i++ { + fb.Fill(0, 0, 255, 0) + } + +} + +func BenchmarkMakeSurfaceImage(b *testing.B) { + surface := cairo.NewSurface(cairo.FORMAT_ARGB32, 1680, 1050) + defer surface.Finish() + for i := 0; i < b.N; i++ { + surface.SetSourceRGBA(1, 1, 1, 1) + surface.Rectangle(0, 0, 240, 80) + surface.Fill() + surface.SelectFontFace("serif", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) + surface.SetFontSize(32.0) + surface.SetSourceRGBA(0, 0, 0, .1) + surface.MoveTo(10.0, 50.0) + surface.ShowText("Hello World") + surface.GetImage() + } + +} + +func BenchmarkDrawImage(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + image := image.NewRGBA(image.Rect(0, 0, fb.Xres, fb.Yres)) + + for i := 0; i < b.N; i++ { + fb.DrawImage(0, 0, image) + } + +} + +func BenchmarkCairoGetData(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + surface := cairo.NewSurface(cairo.FORMAT_ARGB32, fb.Xres, fb.Yres) + defer surface.Finish() + + for i := 0; i < b.N; i++ { + surface.GetData() + } + +} + +func BenchmarkDrawData(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + data := make([]byte, fb.Xres*fb.Yres*4, fb.Xres*fb.Yres*4) + + for i := 0; i < b.N; i++ { + fb.DrawData(0, 0, data, fb.Xres, fb.Yres) + } +} + +func BenchmarkCopyDataByLine(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + data1 := make([]byte, fb.Xres*fb.Yres*4, fb.Xres*fb.Yres*4) + data2 := make([]byte, fb.Xres*fb.Yres*4, fb.Xres*fb.Yres*4) + + for i := 0; i < b.N; i++ { + for y := 0; y < fb.Yres; y++ { + copy(data1[y*fb.Xres*4:(y+1)*fb.Xres*4], data2[y*fb.Xres*4:(y+1)*fb.Xres*4]) + } + } +} + +func BenchmarkCopyDataAllInOne(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + data1 := make([]byte, fb.Xres*fb.Yres*4, fb.Xres*fb.Yres*4) + data2 := make([]byte, fb.Xres*fb.Yres*4, fb.Xres*fb.Yres*4) + + for i := 0; i < b.N; i++ { + copy(data1, data2) + } +} + +func BenchmarkAnimation(b *testing.B) { + fb := NewFramebuffer() + + fb.Init() + defer fb.Release() + + surface := cairo.NewSurface(cairo.FORMAT_ARGB32, fb.Xres, fb.Yres) + defer surface.Finish() + + for i := 0; i < b.N; i++ { + surface.SetSourceRGBA(0, 0, 0, 1) + surface.Rectangle(0, 0, float64(fb.Xres), float64(fb.Yres)) + surface.Fill() + surface.SelectFontFace("serif", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) + surface.SetFontSize(32.0) + surface.SetSourceRGBA(1, 1, 1, 1) + surface.MoveTo(10.0+float64(i), 50.0) + surface.ShowText("道可道 非常道") + data := surface.GetData() + fb.DrawData(0, 0, data, fb.Xres, fb.Yres) + } +}