From d999b4dc8e789d5afe60737a29ad1dad18d5f921 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 7 Feb 2021 21:16:09 +0900 Subject: [PATCH] internal/uidriver/glfw: Bug fix: Enable to specify ebiten.Image to SetWindowIcon Closes #1468 --- examples/windowsize/main.go | 21 +++++++++------ internal/uidriver/glfw/ui.go | 44 +++++++++++++++++++++++++------- internal/uidriver/glfw/window.go | 10 ++------ 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/examples/windowsize/main.go b/examples/windowsize/main.go index 7beb8bc99..d483c72b8 100644 --- a/examples/windowsize/main.go +++ b/examples/windowsize/main.go @@ -66,18 +66,23 @@ var ( func createRandomIconImage() image.Image { const size = 32 - r := byte(rand.Intn(0x100)) - g := byte(rand.Intn(0x100)) - b := byte(rand.Intn(0x100)) - img := image.NewNRGBA(image.Rect(0, 0, size, size)) + rf := float64(rand.Intn(0x100)) + gf := float64(rand.Intn(0x100)) + bf := float64(rand.Intn(0x100)) + img := ebiten.NewImage(size, size) + pix := make([]byte, 4*size*size) for j := 0; j < size; j++ { for i := 0; i < size; i++ { - img.Pix[j*img.Stride+4*i] = r - img.Pix[j*img.Stride+4*i+1] = g - img.Pix[j*img.Stride+4*i+2] = b - img.Pix[j*img.Stride+4*i+3] = byte(float64(i+j) / float64(2*size) * 0xff) + af := float64(i+j) / float64(2*size) + if af > 0 { + pix[4*(j*size+i)] = byte(rf * af) + pix[4*(j*size+i)+1] = byte(gf * af) + pix[4*(j*size+i)+2] = byte(bf * af) + pix[4*(j*size+i)+3] = byte(af * 0xff) + } } } + img.ReplacePixels(pix) return img } diff --git a/internal/uidriver/glfw/ui.go b/internal/uidriver/glfw/ui.go index 4ca6b0de0..d5e7cea2c 100644 --- a/internal/uidriver/glfw/ui.go +++ b/internal/uidriver/glfw/ui.go @@ -68,8 +68,8 @@ type UserInterface struct { initWindowFloating bool initWindowMaximized bool initScreenTransparent bool - initIconImages []image.Image initFocused bool + iconImages []image.Image vsyncInited bool @@ -308,16 +308,16 @@ func (u *UserInterface) setInitScreenTransparent(transparent bool) { u.m.RUnlock() } -func (u *UserInterface) getInitIconImages() []image.Image { +func (u *UserInterface) getIconImages() []image.Image { u.m.RLock() - i := u.initIconImages + i := u.iconImages u.m.RUnlock() return i } -func (u *UserInterface) setInitIconImages(iconImages []image.Image) { +func (u *UserInterface) setIconImages(iconImages []image.Image) { u.m.Lock() - u.initIconImages = iconImages + u.iconImages = iconImages u.m.Unlock() } @@ -685,10 +685,6 @@ func (u *UserInterface) init() error { return err } - if i := u.getInitIconImages(); i != nil { - u.window.SetIcon(i) - } - setPosition := func() { u.iwindow.setPosition(u.getInitWindowPosition()) } @@ -807,6 +803,7 @@ func (u *UserInterface) loop() error { return nil }) }() + for { var unfocused bool @@ -840,6 +837,35 @@ func (u *UserInterface) loop() error { return err } + if imgs := u.getIconImages(); imgs != nil { + u.setIconImages(nil) + + // Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked + // from this goroutine. At works only in between BeginFrame and EndFrame. + go func() { + newImgs := make([]image.Image, len(imgs)) + for i, img := range imgs { + // TODO: If img is not *ebiten.Image, this converting is not necessary. + // However, this package cannot refer *ebiten.Image due to the package + // dependencies. + + b := img.Bounds() + rgba := image.NewRGBA(b) + for j := b.Min.Y; j < b.Max.Y; j++ { + for i := b.Min.X; i < b.Max.X; i++ { + rgba.Set(i, j, img.At(i, j)) + } + } + newImgs[i] = rgba + } + + _ = u.t.Call(func() error { + u.window.SetIcon(newImgs) + return nil + }) + }() + } + // swapBuffers also checks IsGL, so this condition is redundant. // However, (*thread).Call is not good for performance due to channels. // Let's avoid this whenever possible (#1367). diff --git a/internal/uidriver/glfw/window.go b/internal/uidriver/glfw/window.go index 24544868c..363a6b2ae 100644 --- a/internal/uidriver/glfw/window.go +++ b/internal/uidriver/glfw/window.go @@ -249,14 +249,8 @@ func (w *window) SetSize(width, height int) { } func (w *window) SetIcon(iconImages []image.Image) { - if !w.ui.isRunning() { - w.ui.setInitIconImages(iconImages) - return - } - _ = w.ui.t.Call(func() error { - w.ui.window.SetIcon(iconImages) - return nil - }) + // The icons are actually set at (*UserInterface).loop. + w.ui.setIconImages(iconImages) } func (w *window) SetTitle(title string) {