diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..e8004f0 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,28 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.x + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... diff --git a/examples/example.go b/examples/example.go index f41d000..9fc1139 100644 --- a/examples/example.go +++ b/examples/example.go @@ -1,3 +1,5 @@ +//go:build example + package main import ( @@ -20,11 +22,11 @@ func main() { // Fill screen with random noise for i := 0; i < 240; i++ { for j := 0; j < 320; j++ { - f.SetPixel(j, i, rand.Uint32()) + f.Set(j, i, fenster.RGB(rand.Uint32())) } } // Wait for FPS rate - sleep := 16*time.Millisecond - time.Now().Sub(lastFrame) + sleep := 16*time.Millisecond - time.Since(lastFrame) if sleep > 0 { time.Sleep(sleep) } diff --git a/examples/testdata/Testbild.png b/examples/testdata/Testbild.png new file mode 100644 index 0000000..4f97d0b Binary files /dev/null and b/examples/testdata/Testbild.png differ diff --git a/examples/testimage.go b/examples/testimage.go new file mode 100644 index 0000000..31d2e6a --- /dev/null +++ b/examples/testimage.go @@ -0,0 +1,58 @@ +//go:build example + +package main + +import ( + "image" + "image/color/palette" + "image/draw" + "image/png" + "log" + "os" + "time" + + "github.com/zserge/fenster" +) + +func openImage(fname string) (image.Image, error) { + fd, err := os.Open(fname) + if err != nil { + return nil, err + } + defer fd.Close() + return png.Decode(fd) +} + +func main() { + f := fenster.New() + f.Open(320, 240, "Hello") + defer f.Close() + lastFrame := time.Now() + + img, err := openImage("testdata/Testbild.png") + if err != nil { + log.Fatal(err) + } + + // convert to paletted image + pimg := image.NewPaletted(img.Bounds(), palette.Plan9) + draw.Draw(pimg, pimg.Bounds(), img, image.Point{}, draw.Src) + + for f.Loop() { + // If escape is pressed - exit + if f.Key(27) { + break + } + + // rotate palette + pimg.Palette = append(pimg.Palette[1:], pimg.Palette[0]) + draw.Draw(f, f.Bounds(), pimg, image.Point{}, draw.Src) + + // Wait for FPS rate + sleep := 16*time.Millisecond - time.Since(lastFrame) + if sleep > 0 { + time.Sleep(sleep) + } + lastFrame = time.Now() + } +} diff --git a/fenster.go b/fenster.go index d9adb4d..75f52b0 100644 --- a/fenster.go +++ b/fenster.go @@ -9,6 +9,9 @@ package fenster import "C" import ( "fmt" + "image" + "image/color" + "image/draw" "runtime" "unsafe" ) @@ -22,10 +25,33 @@ type Fenster interface { Open(w, h int, title string) error Loop() bool Close() - Pixel(x int, y int) uint32 - SetPixel(x int, y int, rgb uint32) Key(byte) bool Mod() Mods + draw.Image +} + +// RGB Color 00RRGGBB +type RGB uint32 + +func (c RGB) RGBA() (r uint32, g uint32, b uint32, a uint32) { + r = uint32(c>>16) & 0xff + r |= r << 8 + g = uint32(c>>8) & 0xff + g |= g << 8 + b = uint32(c) & 0xff + b |= b << 8 + a = 0xffff + return r, g, b, a +} + +var RGBModel = color.ModelFunc(rgbModel) + +func rgbModel(c color.Color) color.Color { + if _, ok := c.(RGB); ok { + return c + } + r, g, b, _ := c.RGBA() + return RGB((r>>8)<<16 | (g>>8)<<8 | (b >> 8)) } type Mods int @@ -35,6 +61,11 @@ type fenster struct { buf []uint32 } +func (f *fenster) ColorModel() color.Model { return RGBModel } +func (f *fenster) Bounds() image.Rectangle { + return image.Rect(0, 0, int(f.f.width), int(f.f.height)) +} + func New() Fenster { return &fenster{} } func (f *fenster) Open(w, h int, title string) error { f.f.title = C.CString(title) @@ -54,5 +85,7 @@ func (f *fenster) Close() { C.fenster_close(&f.f) } func (f *fenster) Key(code byte) bool { return f.f.keys[code] != 0 } func (f *fenster) Mod() Mods { return Mods(f.f.mod) } -func (f *fenster) Pixel(x, y int) uint32 { return f.buf[y*int(f.f.width)+x] } -func (f *fenster) SetPixel(x, y int, color uint32) { f.buf[y*int(f.f.width)+x] = color } +func (f *fenster) At(x, y int) color.Color { return RGB(f.buf[y*int(f.f.width)+x]) } +func (f *fenster) Set(x, y int, c color.Color) { + f.buf[y*int(f.f.width)+x] = uint32(f.ColorModel().Convert(c).(RGB)) +}