package allg5ui import ( "image" "image/color" "opslag.de/schobers/geom" "opslag.de/schobers/zntg/allg5" "opslag.de/schobers/zntg/ui" ) var _ ui.Renderer = &Renderer{} func NewRenderer(w, h int, opts allg5.NewDisplayOptions) (*Renderer, error) { var err = allg5.Init(allg5.InitAll) if err != nil { return nil, err } disp, err := allg5.NewDisplay(w, h, opts) if err != nil { return nil, err } eq, err := allg5.NewEventQueue() if err != nil { disp.Destroy() return nil, err } eq.RegisterKeyboard() eq.RegisterMouse() eq.RegisterDisplay(disp) return &Renderer{disp, eq, map[string]*font{}}, nil } // Renderer implements ui.Renderer using Allegro 5. type Renderer struct { disp *allg5.Display eq *allg5.EventQueue ft map[string]*font } // Renderer implementation (events) func (r *Renderer) PushEvents(t ui.EventTarget, wait bool) { r.disp.Flip() var ev = eventWait(r.eq, wait) for nil != ev { switch e := ev.(type) { case *allg5.DisplayCloseEvent: t.Handle(&ui.DisplayCloseEvent{EventBase: eventBase(e)}) case *allg5.DisplayResizeEvent: t.Handle(&ui.DisplayResizeEvent{EventBase: eventBase(e), Bounds: geom.RectF32(float32(e.X), float32(e.Y), float32(e.X+e.Width), float32(e.Y+e.Height))}) case *allg5.MouseButtonDownEvent: t.Handle(&ui.MouseButtonDownEvent{MouseEvent: mouseEvent(e.MouseEvent), Button: ui.MouseButton(e.Button)}) case *allg5.MouseButtonUpEvent: t.Handle(&ui.MouseButtonUpEvent{MouseEvent: mouseEvent(e.MouseEvent), Button: ui.MouseButton(e.Button)}) case *allg5.MouseMoveEvent: t.Handle(&ui.MouseMoveEvent{MouseEvent: mouseEvent(e.MouseEvent), MouseWheel: float32(e.DeltaZ)}) } ev = r.eq.Get() } } // Renderer implementation (lifetime) func (r *Renderer) Destroy() error { r.eq.Destroy() r.disp.Destroy() return nil } // Renderer implementation (drawing) func (r *Renderer) Clear(c color.Color) { allg5.ClearToColor(newColor(c)) } func (r *Renderer) CreateImage(im image.Image) (ui.Image, error) { bmp, err := allg5.NewBitmapFromImage(im, true) if err != nil { return nil, err } return &uiImage{bmp}, nil } func (r *Renderer) CreateImagePath(path string) (ui.Image, error) { bmp, err := allg5.LoadBitmap(path) if err != nil { return nil, err } return &uiImage{bmp}, nil } func (r *Renderer) CreateImageSize(w, h float32) (ui.Image, error) { bmp, err := allg5.NewVideoBitmap(int(w), int(h)) if err != nil { return nil, err } return &uiImage{bmp}, nil } func (r *Renderer) DefaultTarget() ui.Image { return &uiImage{r.disp.Target()} } func (r *Renderer) DrawImage(p geom.PointF32, im ui.Image) { bmp := r.mustGetBitmap(im) bmp.Draw(p.X, p.Y) } func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) { allg5.DrawFilledRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c)) } func (r *Renderer) Font(name string) ui.Font { return r.ft[name] } func (r *Renderer) mustGetBitmap(im ui.Image) *allg5.Bitmap { m, ok := im.(*uiImage) if !ok { panic("image must be created on same renderer") } return m.bmp } func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) { allg5.DrawRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c), thickness) } func (r *Renderer) RegisterFont(path, name string, size int) error { var f, err = allg5.LoadTTFFont(path, int(size)) if err != nil { return err } var prev = r.ft[name] if prev != nil { prev.Destroy() } r.ft[name] = newFont(f) return nil } func (r *Renderer) RegisterFonts(path string, fonts ...FontDefinition) error { for _, f := range fonts { err := r.RegisterFont(path, f.Name, f.Size) if err != nil { return err } } return nil } func (r *Renderer) RenderTo(im ui.Image) { bmp := r.mustGetBitmap(im) bmp.SetAsTarget() } func (r *Renderer) RenderToDisplay() { r.disp.SetAsTarget() } func (r *Renderer) Size() geom.PointF32 { return geom.PtF32(float32(r.disp.Width()), float32(r.disp.Height())) } func (r *Renderer) SetWindowTitle(t string) { r.disp.SetWindowTitle(t) } func (r *Renderer) Target() ui.Image { return &uiImage{allg5.CurrentTarget()} } func (r *Renderer) Text(p geom.PointF32, font string, c color.Color, t string) { var f = r.ft[font] if f == nil { return } f.f.Draw(p.X, p.Y, newColor(c), allg5.AlignLeft, t) } // Utility functions func eventWait(eq *allg5.EventQueue, wait bool) allg5.Event { if wait { return eq.GetWait() } return eq.Get() } func eventBase(e allg5.Event) ui.EventBase { return ui.EventBase{StampInSeconds: e.Stamp()} } func mouseEvent(e allg5.MouseEvent) ui.MouseEvent { return ui.MouseEvent{EventBase: eventBase(e), X: float32(e.X), Y: float32(e.Y)} } func newColor(c color.Color) allg5.Color { if c == nil { return newColor(color.Black) } var r, g, b, a = c.RGBA() var r8, g8, b8, a8 = byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8) return allg5.NewColorAlpha(r8, g8, b8, a8) }