diff --git a/ImageWidgets.go b/ImageWidgets.go index 2b553795..4d46b503 100644 --- a/ImageWidgets.go +++ b/ImageWidgets.go @@ -24,6 +24,7 @@ type ImageWidget struct { texture *Texture width float32 height float32 + scale imgui.Vec2 uv0, uv1 imgui.Vec2 tintColor, borderColor color.Color onClick func() @@ -35,6 +36,7 @@ func Image(texture *Texture) *ImageWidget { texture: texture, width: 0, height: 0, + scale: imgui.Vec2{X: 1, Y: 1}, uv0: imgui.Vec2{X: 0, Y: 0}, uv1: imgui.Vec2{X: 1, Y: 1}, tintColor: color.RGBA{255, 255, 255, 255}, @@ -74,6 +76,13 @@ func (i *ImageWidget) Size(width, height float32) *ImageWidget { return i } +// Scale multiply dimensions after size +func (i *ImageWidget) Scale(scaleX, ScaleY float32) *ImageWidget { + // Size image with DPI scaling + i.scale = imgui.Vec2{scaleX, ScaleY} + return i +} + // Build implements Widget interface. func (i *ImageWidget) Build() { if i.width == 0 && i.height == 0 { @@ -96,6 +105,9 @@ func (i *ImageWidget) Build() { size.Y = rect.Y } + size.X = size.X * i.scale.X + size.Y = size.Y * i.scale.Y + if i.texture == nil || i.texture.tex == nil { Dummy(size.X, size.Y).Build() return @@ -107,7 +119,7 @@ func (i *ImageWidget) Build() { mousePos := GetMousePos() if cursorPos.X <= mousePos.X && cursorPos.Y <= mousePos.Y && - cursorPos.X+int(i.width) >= mousePos.X && cursorPos.Y+int(i.height) >= mousePos.Y { + cursorPos.X+int(size.X) >= mousePos.X && cursorPos.Y+int(size.Y) >= mousePos.Y { i.onClick() } } diff --git a/ReflectiveBoundTexture.go b/ReflectiveBoundTexture.go index e8ca83fa..5d6f8024 100644 --- a/ReflectiveBoundTexture.go +++ b/ReflectiveBoundTexture.go @@ -10,20 +10,19 @@ import ( imgui "github.com/AllenDang/cimgui-go" ) -func defaultCanvas() *image.RGBA { - white := color.RGBA{255, 255, 255, 255} - return newImage(800, 600, white) +func defaultSurface() *image.RGBA { + surface, _ := UniformLoader(REFLECTIVE_SURFACE_DEFAULT_WIDTH, REFLECTIVE_SURFACE_DEFAULT_HEIGHT, REFLECTIVE_SURFACE_DEFAULT_COLOR).ServeRGBA() + return surface } -func newImage(width, height int, bgColor color.Color) *image.RGBA { - img := image.NewRGBA(image.Rect(0, 0, width, height)) - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - img.Set(x, y, bgColor) - } - } - return img -} +const ( + REFLECTIVE_SURFACE_DEFAULT_WIDTH = 128 + REFLECTIVE_SURFACE_DEFAULT_HEIGHT = 128 +) + +var ( + REFLECTIVE_SURFACE_DEFAULT_COLOR = color.RGBA{255, 255, 255, 255} +) type ReflectiveBoundTexture struct { Surface *image.RGBA @@ -41,7 +40,7 @@ func (i *ReflectiveBoundTexture) commit() (*ReflectiveBoundTexture, bool) { i.mu.Lock() defer i.mu.Unlock() if i.Surface == nil { - i.Surface = defaultCanvas() + i.Surface = defaultSurface() } var has_changed bool @@ -54,24 +53,6 @@ func (i *ReflectiveBoundTexture) commit() (*ReflectiveBoundTexture, bool) { return i, has_changed } -func (i *ReflectiveBoundTexture) SetSurfaceFn(fn func() *image.RGBA, commit bool) error { - img := fn() - return i.SetSurfaceFromRGBA(img, commit) -} - -func (i *ReflectiveBoundTexture) SetSurfaceFromFillRect(width int, height int, c color.Color, commit bool) error { - img := newImage(width, height, c) - return i.SetSurfaceFromRGBA(img, commit) -} - -func (i *ReflectiveBoundTexture) SetSurfaceFromFile(path string, commit bool) error { - img, err := LoadImage(path) - if err != nil { - return err - } - return i.SetSurfaceFromRGBA(img, commit) -} - func (i *ReflectiveBoundTexture) SetSurfaceFromRGBA(img *image.RGBA, commit bool) error { if img != nil { i.Surface = img @@ -164,6 +145,18 @@ func (i *ReflectiveBoundTexture) bind() { }) } +func (i *ReflectiveBoundTexture) GetSurfaceWidth() int { + return i.Surface.Bounds().Dx() +} + +func (i *ReflectiveBoundTexture) GetSurfaceHeight() int { + return i.Surface.Bounds().Dy() +} + +func (i *ReflectiveBoundTexture) GetSurfaceSize() image.Point { + return i.Surface.Bounds().Size() +} + func (i *ReflectiveBoundTexture) Texture() *Texture { i.commit() return i.tex diff --git a/SurfaceLoaders.go b/SurfaceLoaders.go new file mode 100644 index 00000000..c82277ac --- /dev/null +++ b/SurfaceLoaders.go @@ -0,0 +1,124 @@ +package giu + +import ( + go_ctx "context" + "image" + "image/color" + "image/draw" + "net/http" + "time" +) + +// SurfaceLoader interface +type SurfaceLoader interface { + ServeRGBA() (*image.RGBA, error) +} +type SurfaceLoaderFunc func() (*image.RGBA, error) + +func (i *ReflectiveBoundTexture) LoadSurfaceFunc(fn SurfaceLoaderFunc, commit bool) error { + img, err := fn() + if err != nil { + return err + } + return i.SetSurfaceFromRGBA(img, commit) +} + +func (i *ReflectiveBoundTexture) LoadSurface(loader SurfaceLoader, commit bool) error { + img, err := loader.ServeRGBA() + if err != nil { + return err + } + return i.SetSurfaceFromRGBA(img, commit) +} + +// FileLoader + +type fileLoader struct { + path string +} + +func (f *fileLoader) ServeRGBA() (*image.RGBA, error) { + img, err := LoadImage(f.path) + if err != nil { + return nil, err + } + return img, nil +} + +func FileLoader(path string) SurfaceLoader { + return &fileLoader{ + path: path, + } +} + +func (i *ReflectiveBoundTexture) SetSurfaceFromFile(path string, commit bool) error { + return i.LoadSurface(FileLoader(path), commit) +} + +// UrlLoader + +type urlLoader struct { + url string + timeout time.Duration +} + +func (u *urlLoader) ServeRGBA() (*image.RGBA, error) { + client := &http.Client{Timeout: u.timeout} + req, err := http.NewRequestWithContext(go_ctx.Background(), "GET", u.url, http.NoBody) + if err != nil { + //errorFn(err) + return nil, err + } + resp, err := client.Do(req) + if err != nil { + //errorFn(err) + return nil, err + } + defer resp.Body.Close() + /*defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + errorFn(closeErr) + } + }()*/ + img, _, err := image.Decode(resp.Body) + if err != nil { + //errorFn(err) + return nil, err + } + return ImageToRgba(img), nil +} + +func URLLoader(url string, timeout time.Duration) SurfaceLoader { + return &urlLoader{ + url: url, + timeout: timeout, + } +} + +func (i *ReflectiveBoundTexture) SetSurfaceFromURL(url string, timeout time.Duration, commit bool) error { + return i.LoadSurface(URLLoader(url, timeout), commit) +} + +// UniformLoader +type uniformLoader struct { + width, height int + color color.Color +} + +func (u *uniformLoader) ServeRGBA() (*image.RGBA, error) { + img := image.NewRGBA(image.Rect(0, 0, u.width, u.height)) + draw.Draw(img, img.Bounds(), &image.Uniform{u.color}, image.ZP, draw.Src) + return img, nil +} + +func UniformLoader(width, height int, color color.Color) SurfaceLoader { + return &uniformLoader{ + width: width, + height: height, + color: color, + } +} + +func (i *ReflectiveBoundTexture) SetSurfaceUniform(width int, height int, c color.Color, commit bool) error { + return i.LoadSurface(UniformLoader(width, height, c), commit) +} diff --git a/examples/loadimage/loadimage.go b/examples/loadimage/loadimage.go index 55c7a0dd..d8f62c00 100644 --- a/examples/loadimage/loadimage.go +++ b/examples/loadimage/loadimage.go @@ -1,45 +1,70 @@ package main import ( - "context" "fmt" "image" _ "image/jpeg" _ "image/png" "log" - "net/http" "time" + imgui "github.com/AllenDang/cimgui-go" g "github.com/AllenDang/giu" ) var ( - fallback = &g.ReflectiveBoundTexture{} - gopher = &g.ReflectiveBoundTexture{} - urlWidgetLike = &g.ReflectiveBoundTexture{} - rgba *image.RGBA + fromrgba = &g.ReflectiveBoundTexture{} + fromfile = &g.ReflectiveBoundTexture{} + fromurl = &g.ReflectiveBoundTexture{} + rgba *image.RGBA + sonicOffsetX = int32(1180) + sonicOffsetY = int32(580) ) func loop() { - - urlWidgetLike.SetSurfaceFn(downloadUrlFn("https://static.wikia.nocookie.net/smashbros/images/0/0e/Art_Sonic_TSR.png/revision/latest?cb=20200210122913&path-prefix=fr", time.Second*10), true) + var start_pos image.Point + var window_size imgui.Vec2 g.SingleWindow().Layout( - g.Label("Display image from texture"), - gopher.ToImageWidget(), - g.Label("Display image from fallback"), - fallback.ToImageWidget().OnClick(func() { + g.Custom(func() { + start_pos = g.GetCursorScreenPos() + window_size = imgui.WindowSize() + }), + g.Label("Display wich has size of contentAvaiable (stretch)"), + fromfile.ToImageWidget().OnClick(func() { + fmt.Println("contentAvailable image was clicked") + }).Size(-1, -1), + + g.Label("Display image from preloaded rgba"), + fromrgba.ToImageWidget().OnClick(func() { fmt.Println("rgba image was clicked") - }).Size(600, 400), - g.Label("Display image from gopher"), - gopher.ToImageWidget().OnClick(func() { + }), + + g.Label("Display image from file"), + fromfile.ToImageWidget().OnClick(func() { fmt.Println("image from file was clicked") - }).Size(860, 561), + }), - // TODO Rewrite via func (i *ReflectiveBoundTexture) SetSurfaceFn(fn func() *image.RGBA, commit bool) error { - g.Label("Display image from url (wait few seconds to download)"), - g.ImageWithURL("https://png.pngitem.com/pimgs/s/3-36108_gopher-golang-hd-png-download.png").OnClick(func() { + g.Label("Display image from url + 0.25 scale"), + fromurl.ToImageWidget().OnClick(func() { fmt.Println("image from url clicked") - }).Size(300, 200), + }).Scale(0.25, 0.25), + + g.Label("Advanced Drawing manipulation"), + g.DragInt("Sonic Offset X", &sonicOffsetX, 0, 1280), + g.DragInt("Sonic Offset Y", &sonicOffsetY, 0, 720), + g.Custom(func() { + size := fromurl.GetSurfaceSize() + sonicOffset := image.Point{int(sonicOffsetX), int(sonicOffsetY)} + pos_with_offset := start_pos.Add(sonicOffset) + computed_posX := (float32(pos_with_offset.X)) + imgui.ScrollX() + computed_posY := (float32(pos_with_offset.Y)) + imgui.ScrollY() + //cur_pos := g.GetCursorPos() + scale := imgui.Vec2{0.10, 0.10} + p_min := imgui.Vec2{computed_posX, computed_posY} + p_max := imgui.Vec2{computed_posX + float32(size.X)*scale.X, computed_posY + float32(size.Y)*scale.Y} + imgui.ForegroundDrawList().AddImage(fromurl.Texture().ID(), p_min, p_max) + }), + /* g.Label("Display images from url with loading and fallback"), g.Label("Display image from url without placeholder (no size when loading)"), @@ -76,39 +101,10 @@ func main() { if err != nil { log.Fatalf("Cannot loadIamge fallback.png") } - gopher.SetSurfaceFromFile("gopher.png", false) - fallback.SetSurfaceFromRGBA(rgba, false) + fromfile.SetSurfaceFromFile("gopher.png", false) + fromrgba.SetSurfaceFromRGBA(rgba, false) + fromurl.SetSurfaceFromURL("https://static.wikia.nocookie.net/smashbros/images/0/0e/Art_Sonic_TSR.png/revision/latest?cb=20200210122913&path-prefix=fr", time.Second*5, false) - wnd := g.NewMasterWindow("Load Image", 600, 500, g.MasterWindowFlagsNotResizable) - //wnd.SetIcon(rgba) + wnd := g.NewMasterWindow("Load Image", 1280, 720, 0) wnd.Run(loop) } - -func downloadUrlFn(imgUrl string, timeout time.Duration) func() *image.RGBA { - return func() *image.RGBA { - client := &http.Client{Timeout: timeout} - req, err := http.NewRequestWithContext(context.Background(), "GET", imgUrl, http.NoBody) - if err != nil { - //errorFn(err) - return nil - } - resp, err := client.Do(req) - if err != nil { - //errorFn(err) - return nil - } - defer resp.Body.Close() - /*defer func() { - if closeErr := resp.Body.Close(); closeErr != nil { - errorFn(closeErr) - } - }()*/ - img, _, err := image.Decode(resp.Body) - if err != nil { - //errorFn(err) - return nil - } - return g.ImageToRgba(img) - - } -} diff --git a/go.mod b/go.mod index 57974585..3ff545a4 100644 --- a/go.mod +++ b/go.mod @@ -24,3 +24,5 @@ require ( golang.org/x/sys v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +