From 48aaf30182cc3531482d402d51e49d49c37cf6d5 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Tue, 12 May 2020 23:03:43 +0200 Subject: [PATCH] Renamed Image{,s} to Texture{,s}. --- allg5ui/image.go | 20 +++--- allg5ui/renderer.go | 42 ++++++------ ui/buffer.go | 20 +++--- ui/button.go | 10 +-- ui/checkbox.go | 10 +-- ui/context.go | 16 ++--- ui/examples/01_basic/basic.go | 4 +- ui/icon.go | 34 +++++----- ui/images.go | 123 ---------------------------------- ui/renderer.go | 16 ++--- ui/slider.go | 8 +-- ui/{image.go => texture.go} | 4 +- ui/textures.go | 123 ++++++++++++++++++++++++++++++++++ ui/ui.go | 2 +- 14 files changed, 216 insertions(+), 216 deletions(-) delete mode 100644 ui/images.go rename ui/{image.go => texture.go} (61%) create mode 100644 ui/textures.go diff --git a/allg5ui/image.go b/allg5ui/image.go index e732f0c..aa82de3 100644 --- a/allg5ui/image.go +++ b/allg5ui/image.go @@ -7,24 +7,24 @@ import ( "opslag.de/schobers/zntg/ui" ) -var _ ui.Image = &uiImage{} +var _ ui.Texture = &texture{} -type uiImage struct { +type texture struct { bmp *allg5.Bitmap } -func (i *uiImage) Destroy() { - i.bmp.Destroy() +func (t *texture) Destroy() { + t.bmp.Destroy() } -func (i *uiImage) Height() float32 { - return float32(i.bmp.Height()) +func (t *texture) Height() float32 { + return float32(t.bmp.Height()) } -func (i *uiImage) Image() image.Image { - return i.bmp.Image() +func (t *texture) Texture() image.Image { + return t.bmp.Image() } -func (i *uiImage) Width() float32 { - return float32(i.bmp.Width()) +func (t *texture) Width() float32 { + return float32(t.bmp.Width()) } diff --git a/allg5ui/renderer.go b/allg5ui/renderer.go index bc52fcc..b08894c 100644 --- a/allg5ui/renderer.go +++ b/allg5ui/renderer.go @@ -130,44 +130,44 @@ func (r *Renderer) Clear(c color.Color) { allg5.ClearToColor(newColor(c)) } -func (r *Renderer) CreateImage(im image.Image) (ui.Image, error) { +func (r *Renderer) CreateTexture(im image.Image) (ui.Texture, error) { bmp, err := allg5.NewBitmapFromImage(im, true) if err != nil { return nil, err } - return &uiImage{bmp}, nil + return &texture{bmp}, nil } -func (r *Renderer) CreateImagePath(path string) (ui.Image, error) { +func (r *Renderer) CreateTexturePath(path string) (ui.Texture, error) { bmp, err := allg5.LoadBitmap(path) if err != nil { return nil, err } - return &uiImage{bmp}, nil + return &texture{bmp}, nil } -func (r *Renderer) CreateImageSize(w, h float32) (ui.Image, error) { +func (r *Renderer) CreateTextureSize(w, h float32) (ui.Texture, error) { bmp, err := allg5.NewVideoBitmap(int(w), int(h)) if err != nil { return nil, err } - return &uiImage{bmp}, nil + return &texture{bmp}, nil } -func (r *Renderer) DefaultTarget() ui.Image { - return &uiImage{r.disp.Target()} +func (r *Renderer) DefaultTarget() ui.Texture { + return &texture{r.disp.Target()} } func (r *Renderer) Display() *allg5.Display { return r.disp } -func (r *Renderer) DrawImage(im ui.Image, p geom.PointF32) { - bmp := r.mustGetBitmap(im) +func (r *Renderer) DrawTexture(texture ui.Texture, p geom.PointF32) { + bmp := r.mustGetBitmap(texture) x, y := snap(p) bmp.Draw(x, y) } -func (r *Renderer) DrawImageOptions(im ui.Image, p geom.PointF32, opts ui.DrawOptions) { - bmp := r.mustGetBitmap(im) +func (r *Renderer) DrawTextureOptions(texture ui.Texture, p geom.PointF32, opts ui.DrawOptions) { + bmp := r.mustGetBitmap(texture) var o allg5.DrawOptions if opts.Tint != nil { tint := newColor(opts.Tint) @@ -188,12 +188,12 @@ 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) +func (r *Renderer) mustGetBitmap(t ui.Texture) *allg5.Bitmap { + texture, ok := t.(*texture) if !ok { panic("image must be created on same renderer") } - return m.bmp + return texture.bmp } func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) { @@ -225,8 +225,8 @@ func (r *Renderer) RegisterFonts(path string, fonts ...FontDefinition) error { return nil } -func (r *Renderer) RenderTo(im ui.Image) { - bmp := r.mustGetBitmap(im) +func (r *Renderer) RenderTo(texture ui.Texture) { + bmp := r.mustGetBitmap(texture) bmp.SetAsTarget() } @@ -238,8 +238,8 @@ func (r *Renderer) Size() geom.PointF32 { return geom.PtF32(float32(r.disp.Width()), float32(r.disp.Height())) } -func (r *Renderer) SetIcon(im ui.Image) { - bmp := r.mustGetBitmap(im) +func (r *Renderer) SetIcon(texture ui.Texture) { + bmp := r.mustGetBitmap(texture) r.disp.SetIcon(bmp) } @@ -255,8 +255,8 @@ func (r *Renderer) SetWindowTitle(t string) { r.disp.SetWindowTitle(t) } -func (r *Renderer) Target() ui.Image { - return &uiImage{allg5.CurrentTarget()} +func (r *Renderer) Target() ui.Texture { + return &texture{allg5.CurrentTarget()} } func (r *Renderer) text(p geom.PointF32, font string, c color.Color, t string, align allg5.HorizontalAlignment) { diff --git a/ui/buffer.go b/ui/buffer.go index 74dfe7c..a949a3e 100644 --- a/ui/buffer.go +++ b/ui/buffer.go @@ -5,38 +5,38 @@ import "opslag.de/schobers/geom" type RenderBufferFn func(ctx Context, size geom.PointF32) type Buffer struct { - im Image - size geom.PointF32 + texture Texture + size geom.PointF32 } func (b *Buffer) Update(ctx Context, size geom.PointF32) error { - if b.im != nil { + if b.texture != nil { if size == b.size { return nil } - b.im.Destroy() - b.im = nil + b.texture.Destroy() + b.texture = nil b.size = geom.ZeroPtF32 } - im, err := ctx.Renderer().CreateImageSize(size.X, size.Y) + texture, err := ctx.Renderer().CreateTextureSize(size.X, size.Y) if err != nil { return err } - b.im = im + b.texture = texture b.size = size return nil } func (b *Buffer) Render(ctx Context, pos geom.PointF32, fn RenderBufferFn) { - if b.im == nil { + if b.texture == nil { return } renderer := ctx.Renderer() currTarget := renderer.Target() - renderer.RenderTo(b.im) + renderer.RenderTo(b.texture) fn(ctx, b.size) renderer.RenderTo(currTarget) - renderer.DrawImage(b.im, pos) + renderer.DrawTexture(b.texture, pos) } type BufferControl struct { diff --git a/ui/button.go b/ui/button.go index d720357..de20edd 100644 --- a/ui/button.go +++ b/ui/button.go @@ -10,7 +10,7 @@ type Button struct { ControlBase HoverColor color.Color - Icon Image + Icon Texture IconScale float32 Text string Type ButtonType @@ -27,8 +27,8 @@ const ( func BuildButton(text string, fn func(b *Button)) *Button { return BuildIconButton(nil, text, fn) } -func BuildIconButton(i Image, text string, fn func(b *Button)) *Button { - var b = &Button{Text: text, Icon: i} +func BuildIconButton(icon Texture, text string, fn func(b *Button)) *Button { + var b = &Button{Text: text, Icon: icon} if fn != nil { fn(b) } @@ -133,9 +133,9 @@ func (b *Button) Render(ctx Context) { bounds = bounds.Inset(pad) pos := bounds.Min if b.Icon != nil && b.Icon.Height() > 0 { - icon, _ := ctx.Images().ScaledHeight(b.Icon, b.scale(bounds.Dy())) + icon, _ := ctx.Textures().ScaledHeight(b.Icon, b.scale(bounds.Dy())) if icon != nil { - ctx.Renderer().DrawImageOptions(icon, geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-icon.Height())), DrawOptions{Tint: textColor}) + ctx.Renderer().DrawTextureOptions(icon, geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-icon.Height())), DrawOptions{Tint: textColor}) pos.X += icon.Width() + pad } } diff --git a/ui/checkbox.go b/ui/checkbox.go index 5358701..12d6934 100644 --- a/ui/checkbox.go +++ b/ui/checkbox.go @@ -30,12 +30,12 @@ func (c *Checkbox) desiredSize(ctx Context) geom.PointF32 { if len(c.Text) != 0 { w += pad + font.WidthOf(c.Text) } - icon, _ := ctx.Images().ScaledHeight(c.getOrCreateNormalIcon(ctx), h) + icon, _ := ctx.Textures().ScaledHeight(c.getOrCreateNormalIcon(ctx), h) w += pad + icon.Width() return geom.PtF32(w+pad, pad+h+pad) } -func (c *Checkbox) icon(ctx Context) Image { +func (c *Checkbox) icon(ctx Context) Texture { if c.Selected { return GetOrCreateIcon(ctx, "ui-default-checkbox-selected", c.selectedIcon) } else if c.over { @@ -44,7 +44,7 @@ func (c *Checkbox) icon(ctx Context) Image { return c.getOrCreateNormalIcon(ctx) } -func (c *Checkbox) getOrCreateNormalIcon(ctx Context) Image { +func (c *Checkbox) getOrCreateNormalIcon(ctx Context) Texture { return GetOrCreateIcon(ctx, "ui-default-checkbox", c.normalIcon) } @@ -125,13 +125,13 @@ func (c *Checkbox) Render(ctx Context) { var pad = style.Dimensions.TextPadding bounds = bounds.Inset(pad) pos := bounds.Min - icon, _ := ctx.Images().ScaledHeight(c.icon(ctx), bounds.Dy()) + icon, _ := ctx.Textures().ScaledHeight(c.icon(ctx), bounds.Dy()) if icon != nil { iconColor := fore if c.Selected && c.Font.Color == nil { iconColor = palette.Primary } - ctx.Renderer().DrawImageOptions(icon, geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-icon.Height())), DrawOptions{Tint: iconColor}) + ctx.Renderer().DrawTextureOptions(icon, geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-icon.Height())), DrawOptions{Tint: iconColor}) pos.X += icon.Width() + pad } if len(c.Text) != 0 { diff --git a/ui/context.go b/ui/context.go index 3c08148..c5273ca 100644 --- a/ui/context.go +++ b/ui/context.go @@ -3,7 +3,7 @@ package ui type Context interface { Animate() HasQuit() bool - Images() *Images + Textures() *Textures Quit() Renderer() Renderer Style() *Style @@ -13,12 +13,12 @@ var _ Context = &context{} var _ EventTarget = &context{} type context struct { - animate bool - quit chan struct{} - r Renderer - view Control - ims *Images - style *Style + animate bool + quit chan struct{} + r Renderer + view Control + textures *Textures + style *Style } func (c *context) Animate() { c.animate = true } @@ -32,7 +32,7 @@ func (c *context) HasQuit() bool { } } -func (c *context) Images() *Images { return c.ims } +func (c *context) Textures() *Textures { return c.textures } func (c *context) Quit() { if !c.HasQuit() { diff --git a/ui/examples/01_basic/basic.go b/ui/examples/01_basic/basic.go index 98a1693..b9d3931 100644 --- a/ui/examples/01_basic/basic.go +++ b/ui/examples/01_basic/basic.go @@ -13,7 +13,7 @@ import ( type basic struct { ui.StackPanel - plus ui.Image + plus ui.Texture } func (b *basic) Init(ctx ui.Context) error { @@ -73,7 +73,7 @@ func run() error { if err != nil { return err } - plus, err := render.CreateImagePath("../resources/images/plus.png") + plus, err := render.CreateTexturePath("../resources/images/plus.png") if err != nil { return err } diff --git a/ui/icon.go b/ui/icon.go index ff80722..3e7351d 100644 --- a/ui/icon.go +++ b/ui/icon.go @@ -10,27 +10,27 @@ import ( type ImagePixelTestFn func(geom.PointF32) bool type ImageAlphaPixelTestFn func(geom.PointF32) uint8 -func createImage(ctx Context, image image.Image) Image { - im, err := ctx.Renderer().CreateImage(image) +func createTexture(ctx Context, image image.Image) Texture { + texture, err := ctx.Renderer().CreateTexture(image) if err != nil { return nil } - return im + return texture } -func CreateIcon(ctx Context, test ImagePixelTestFn) Image { +func CreateIcon(ctx Context, test ImagePixelTestFn) Texture { icon := DrawIcon(test) - return createImage(ctx, icon) + return createTexture(ctx, icon) } -func CreateImage(ctx Context, size geom.Point, test ImagePixelTestFn) Image { +func CreateTexture(ctx Context, size geom.Point, test ImagePixelTestFn) Texture { image := DrawImage(size, test) - return createImage(ctx, image) + return createTexture(ctx, image) } -func CreateImageAlpha(ctx Context, size geom.Point, test ImageAlphaPixelTestFn) Image { +func CreateTextureAlpha(ctx Context, size geom.Point, test ImageAlphaPixelTestFn) Texture { image := DrawImageAlpha(size, test) - return createImage(ctx, image) + return createTexture(ctx, image) } func DrawIcon(test ImagePixelTestFn) image.Image { @@ -66,18 +66,18 @@ func DrawImageAlpha(size geom.Point, test ImageAlphaPixelTestFn) image.Image { return icon } -func GetOrCreateIcon(ctx Context, name string, testFactory func() ImagePixelTestFn) Image { - im := ctx.Images().Image(name) - if im != nil { - return im +func GetOrCreateIcon(ctx Context, name string, testFactory func() ImagePixelTestFn) Texture { + texture := ctx.Textures().Texture(name) + if texture != nil { + return texture } test := testFactory() - im = CreateIcon(ctx, test) - if im == nil { + texture = CreateIcon(ctx, test) + if texture == nil { return nil } - ctx.Images().AddImage(name, im) - return im + ctx.Textures().AddTexture(name, texture) + return texture } func IconSize() geom.Point { diff --git a/ui/images.go b/ui/images.go deleted file mode 100644 index 4bf0575..0000000 --- a/ui/images.go +++ /dev/null @@ -1,123 +0,0 @@ -package ui - -import ( - "image" - - "github.com/nfnt/resize" - "opslag.de/schobers/geom" -) - -type CreateImageFn func() (image.Image, error) - -func ScaleImage(render Renderer, im Image, scale float32) Image { - w := uint(im.Width() * scale) - if w == 0 { - return nil - } - scaled := resize.Resize(w, 0, im.Image(), resize.Bilinear) - res, err := render.CreateImage(scaled) - if err != nil { - return nil - } - return res -} - -type Images struct { - render Renderer - ims map[string]Image - scaled map[Image]ScaledImages -} - -func NewImages(render Renderer) *Images { - return &Images{render, map[string]Image{}, map[Image]ScaledImages{}} -} - -func (i *Images) AddImage(name string, im Image) { - curr := i.ims[name] - if curr != nil { - curr.Destroy() - } - i.ims[name] = im -} - -func (i *Images) AddImageFn(name string, create CreateImageFn) error { - im, err := create() - if err != nil { - return err - } - return i.AddImageNative(name, im) -} - -func (i *Images) AddImageNative(name string, im image.Image) error { - m, err := i.render.CreateImage(im) - if err != nil { - return err - } - i.AddImage(name, m) - return nil -} - -func (i *Images) Destroy() { - for _, im := range i.ims { - im.Destroy() - } - i.ims = nil - for _, ims := range i.scaled { - ims.Destroy() - } - i.scaled = nil -} - -func (i *Images) Image(name string) Image { - im, ok := i.ims[name] - if ok { - return im - } - return nil -} - -func (i *Images) Scaled(im Image, scale float32) Image { - if scale <= 0 { - return nil - } - if scale == 1 { - return im - } - ims := i.scaled[im] - if ims == nil { - ims = make(ScaledImages) - } else { - scaled := ims[scale] - if scaled != nil { - return scaled - } - } - scaled := ScaleImage(i.render, im, scale) - ims[scale] = scaled - i.scaled[im] = ims - return scaled -} - -func (i *Images) ScaledHeight(im Image, height float32) (Image, float32) { - scale := height / im.Height() - if geom.IsNaN32(scale) { - return nil, 0 - } - return i.Scaled(im, scale), scale -} - -func (i *Images) ScaledByName(name string, scale float32) Image { - im := i.Image(name) - if im == nil { - return nil - } - return i.Scaled(im, scale) -} - -type ScaledImages map[float32]Image - -func (i ScaledImages) Destroy() { - for _, im := range i { - im.Destroy() - } -} diff --git a/ui/renderer.go b/ui/renderer.go index 4ac34fd..379cbe2 100644 --- a/ui/renderer.go +++ b/ui/renderer.go @@ -17,21 +17,21 @@ type Renderer interface { // Drawing Clear(c color.Color) - CreateImage(m image.Image) (Image, error) - CreateImagePath(path string) (Image, error) - CreateImageSize(w, h float32) (Image, error) - DefaultTarget() Image - DrawImage(im Image, p geom.PointF32) - DrawImageOptions(im Image, p geom.PointF32, opts DrawOptions) + CreateTexture(m image.Image) (Texture, error) + CreateTexturePath(path string) (Texture, error) + CreateTextureSize(w, h float32) (Texture, error) + DefaultTarget() Texture + DrawTexture(t Texture, p geom.PointF32) + DrawTextureOptions(t Texture, p geom.PointF32, opts DrawOptions) FillRectangle(r geom.RectangleF32, c color.Color) Font(name string) Font Rectangle(r geom.RectangleF32, c color.Color, thickness float32) RegisterFont(name, path string, size int) error - RenderTo(Image) + RenderTo(Texture) RenderToDisplay() SetMouseCursor(c MouseCursor) Size() geom.PointF32 - Target() Image + Target() Texture Text(p geom.PointF32, font string, color color.Color, text string) TextAlign(p geom.PointF32, font string, color color.Color, text string, align HorizontalAlignment) } diff --git a/ui/slider.go b/ui/slider.go index 324f917..95c594e 100644 --- a/ui/slider.go +++ b/ui/slider.go @@ -134,17 +134,17 @@ func (s *Slider) OnValueChanged(fn func(float32)) { type sliderHandle struct { ControlBase - handle Image + handle Texture } -func (h *sliderHandle) im(ctx Context) Image { +func (h *sliderHandle) texture(ctx Context) Texture { if h.handle != nil { return h.handle } size := h.Bounds().Size() center := geom.PtF32(.5*size.X-.5, .5*size.Y-.5) radius := 0.5 * geom.Min32(size.X, size.Y) - h.handle = CreateImageAlpha(ctx, geom.Pt(int(size.X), int(size.Y)), func(p geom.PointF32) uint8 { + h.handle = CreateTextureAlpha(ctx, geom.Pt(int(size.X), int(size.Y)), func(p geom.PointF32) uint8 { dist := center.Distance(p) if dist < radius { if dist < (radius - 1) { @@ -169,5 +169,5 @@ func (h *sliderHandle) Render(ctx Context) { if h.IsOver() { color = ctx.Style().Palette.PrimaryLight } - ctx.Renderer().DrawImageOptions(h.im(ctx), h.Bounds().Min, DrawOptions{Tint: color}) + ctx.Renderer().DrawTextureOptions(h.texture(ctx), h.Bounds().Min, DrawOptions{Tint: color}) } diff --git a/ui/image.go b/ui/texture.go similarity index 61% rename from ui/image.go rename to ui/texture.go index b9983f0..7e50933 100644 --- a/ui/image.go +++ b/ui/texture.go @@ -2,9 +2,9 @@ package ui import "image" -type Image interface { +type Texture interface { Destroy() Height() float32 - Image() image.Image + Texture() image.Image Width() float32 } diff --git a/ui/textures.go b/ui/textures.go new file mode 100644 index 0000000..3f3fbe1 --- /dev/null +++ b/ui/textures.go @@ -0,0 +1,123 @@ +package ui + +import ( + "image" + + "github.com/nfnt/resize" + "opslag.de/schobers/geom" +) + +type CreateImageFn func() (image.Image, error) + +func ScaleTexture(render Renderer, texture Texture, scale float32) Texture { + w := uint(texture.Width() * scale) + if w == 0 { + return nil + } + scaled := resize.Resize(w, 0, texture.Texture(), resize.Bilinear) + res, err := render.CreateTexture(scaled) + if err != nil { + return nil + } + return res +} + +type Textures struct { + render Renderer + textures map[string]Texture + scaled map[Texture]ScaledTextures +} + +func NewTextures(render Renderer) *Textures { + return &Textures{render, map[string]Texture{}, map[Texture]ScaledTextures{}} +} + +func (t *Textures) AddTexture(name string, texture Texture) { + curr := t.textures[name] + if curr != nil { + curr.Destroy() + } + t.textures[name] = texture +} + +func (t *Textures) AddTextureFn(name string, create CreateImageFn) error { + im, err := create() + if err != nil { + return err + } + return t.AddTextureNative(name, im) +} + +func (t *Textures) AddTextureNative(name string, im image.Image) error { + texture, err := t.render.CreateTexture(im) + if err != nil { + return err + } + t.AddTexture(name, texture) + return nil +} + +func (t *Textures) Destroy() { + for _, texture := range t.textures { + texture.Destroy() + } + t.textures = nil + for _, textures := range t.scaled { + textures.Destroy() + } + t.scaled = nil +} + +func (t *Textures) Texture(name string) Texture { + texture, ok := t.textures[name] + if ok { + return texture + } + return nil +} + +func (t *Textures) Scaled(texture Texture, scale float32) Texture { + if scale <= 0 { + return nil + } + if scale == 1 { + return texture + } + textures := t.scaled[texture] + if textures == nil { + textures = make(ScaledTextures) + } else { + scaled := textures[scale] + if scaled != nil { + return scaled + } + } + scaled := ScaleTexture(t.render, texture, scale) + textures[scale] = scaled + t.scaled[texture] = textures + return scaled +} + +func (t *Textures) ScaledHeight(textures Texture, height float32) (Texture, float32) { + scale := height / textures.Height() + if geom.IsNaN32(scale) { + return nil, 0 + } + return t.Scaled(textures, scale), scale +} + +func (t *Textures) ScaledByName(name string, scale float32) Texture { + textures := t.Texture(name) + if textures == nil { + return nil + } + return t.Scaled(textures, scale) +} + +type ScaledTextures map[float32]Texture + +func (t ScaledTextures) Destroy() { + for _, textures := range t { + textures.Destroy() + } +} diff --git a/ui/ui.go b/ui/ui.go index 13bb503..a1b3fca 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -13,7 +13,7 @@ func Run(r Renderer, s *Style, view Control) error { // RunWait runs the application loop and conditionally waits on events before rendering. func RunWait(r Renderer, s *Style, view Control, wait bool) error { - ctx := &context{quit: make(chan struct{}), r: r, style: s, view: view, ims: NewImages(r)} + ctx := &context{quit: make(chan struct{}), r: r, style: s, view: view, textures: NewTextures(r)} root, ok := view.(RootControl) if ok { err := root.Init(ctx)