Skip to content

Fyne Thumbnail Proposal

Stephen M Houston edited this page Aug 13, 2020 · 9 revisions

At some point in the near future Fyne will need the ability to create thumbnails and load them as well as load them from cache. I am not familiar with thumbnailing outside of Linux using the xdg/fdo thumbnail manager spec. At a brief glance it would seem to use Window's system we would need to do it natively using syscalls. I would imagine we would need to do thumbnailing natively on ios/android as well due to storage locations and permissions. For now I have a working solution in FyFoto that follows the xdg/fdo spec and adapts to fallbacks for cross platform. Thumbnailing is done in a go routine and this is what the current code looks like: package main

import ( "crypto/md5" "encoding/hex" "fmt" "image" "image/png" "math" "os" "os/user" "time"

`"fyne.io/fyne"`
`"fyne.io/fyne/canvas"`

`"golang.org/x/image/draw"`

)

func thumbnail(ff *FyFoto, thumbQueue <-chan gridImage, quitQueue <-chan string) { for { select { case <-quitQueue: return case gi := <-thumbQueue: file := gi.imageFile base := "" xdgcache := os.Getenv("XDG_CACHE_HOME") if xdgcache == "" { usr, err := user.Current() if err != nil { fmt.Println("Could not find the current user") return } base = usr.HomeDir + "/.cache" } else { base = xdgcache } os.Mkdir(base, 0700) thumbDir := base + "/thumbnails" os.Mkdir(thumbDir, 0700) thumbDir += "/normal" os.Mkdir(thumbDir, 0700)

		`uri := []byte("file://" + file)`
		`newfileMD5 := md5.New()`
		`newfileMD5.Write(uri)`
		`newfile := hex.EncodeToString(newfileMD5.Sum(nil))`
		`destfile := thumbDir + "/" + newfile + ".png"`

		`needThumb := 1`
		`th, err := os.Stat(destfile)`
		`if !os.IsNotExist(err) {`
			`orig, _ := os.Stat(file)`
			`modThumb := th.ModTime()`
			`modOrig := orig.ModTime()`
			`diff := modThumb.Sub(modOrig)`
			`if diff > (time.Duration(0) * time.Second) {`
				`needThumb = 0`
			`}`
		`}`
		`if needThumb == 1 {`
			`img, err := os.Open(file)`
			`if err != nil {`
				`fmt.Println("Could not open image to thumbnail")`
				`img.Close()`
				`break`
			`}`
			`src, _, err := image.Decode(img)`
			`if err != nil {`
				`fmt.Println("Could not decode source image for thumbnail")`
				`img.Close()`
				`break`
			`}`
			`img.Close()`
			`img, err = os.Open(file)`
			`if err != nil {`
				`fmt.Println("Could not open image to thumbnail")`
				`img.Close()`
				`break`
			`}`
			`cfg, _, err := image.DecodeConfig(img)`
			`if err != nil {`
				`fmt.Println("Could not get original image size")`
				`img.Close()`
				`break`
			`}`
			`img.Close()`
			`newWidth, newHeight := 128, 128`
			`if cfg.Width > cfg.Height {`
				`scale := float64(cfg.Width) / float64(cfg.Height)`
				`newHeight = int(math.Round(float64(newWidth) / float64(scale)))`
			`} else if cfg.Height > cfg.Width {`
				`scale := float64(cfg.Height) / float64(cfg.Width)`
				`newWidth = int(math.Round(float64(newHeight) / float64(scale)))`
			`}`
			`dest := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))`
			`draw.NearestNeighbor.Scale(dest, dest.Bounds(), src, src.Bounds(), draw.Src, nil)`

			`out, err := os.Create(destfile)`
			`if err != nil {`
				`fmt.Println("Could not create thumbnail destination file")`
			`}`
			`err = png.Encode(out, dest)`
			`if err != nil {`
				`fmt.Println("Could not encode png for thumbnail")`
			`}`
		`}`
		`if &gi != nil && gi.imageDir == ff.currentDir {`
			`gi.imageObject = canvas.NewImageFromFile(destfile)`
			`gi.imageObject.FillMode = canvas.ImageFillContain`
			`size := int(math.Floor(float64(128 * ff.window.Canvas().Scale())))`
			`gi.imageObject.SetMinSize(fyne.NewSize(size, size))`
			`gi.Append(gi.imageObject)`

			`ff.images.AddObject(&gi)`
			`canvas.Refresh(ff.images)`
		`}`
	`}`
`}`

}

This would not be hard to adapt into canvas/image or widget/icon. What would be needed:

  • A new API such as SetThumbnail(thumbnail bool) that if true would take the image resource and thumbnail it/get thumbnail from cache and use that as the displayed imaged.

Considerations:

  • Is this cross platform enough where we take the trade off of potentially adding additional storage/duplication of thumbnails on platforms other than linux by adapting the fdo/xdg spec cross platform?
  • Should we consider doing the thumbnails natively on platforms that require them be done that way including syscalls?
  • Should we add in an API to switch the thumbnail size between the specs NORMAL and LARGE choices or should we just stick to NORMAL?
  • What milestone would this need to be a part of?

Welcome to the Fyne wiki.

Project documentation

Getting involved

Platform details

Clone this wiki locally