commit f40c9db

delthas  ·  2025-09-26 10:04:09 +0000 UTC
parent 4173dac
Optimize loading previews of EXIF-rotated images
3 files changed,  +69, -27
M app.go
M app.go
+2, -1
 1@@ -568,6 +568,7 @@ func (app *App) handleUIEvent(ev interface{}) bool {
 2 	// TODO: eat QuitEvent here?
 3 	switch ev := ev.(type) {
 4 	case vaxis.Resize:
 5+		app.win.SetWinPixels(ev.XPixel, ev.YPixel)
 6 		app.win.Resize()
 7 	case vaxis.PasteStartEvent:
 8 		app.pasting = true
 9@@ -1160,7 +1161,7 @@ func (app *App) fetchImage(link string) (image.Image, error) {
10 	if err != nil {
11 		return nil, err
12 	}
13-	img, _, err := ui.DecodeImage(res.Body)
14+	img, _, err := app.win.DecodeImage(res.Body)
15 	res.Body.Close()
16 	if err != nil {
17 		return nil, err
+0, -26
 1@@ -1,12 +1,8 @@
 2 package ui
 3 
 4 import (
 5-	"bytes"
 6 	"encoding/binary"
 7-	"image"
 8 	"io"
 9-
10-	"github.com/disintegration/imaging"
11 )
12 
13 func exifOrientation(r io.Reader) int {
14@@ -129,25 +125,3 @@ func exifOrientation(r io.Reader) int {
15 	}
16 	return 0 // Missing orientation tag.
17 }
18-
19-func DecodeImage(r io.Reader) (image.Image, string, error) {
20-	var b bytes.Buffer
21-	tr := io.TeeReader(io.LimitReader(r, 1<<20), &b)
22-	o := exifOrientation(tr)
23-	r = io.MultiReader(&b, r)
24-
25-	img, format, err := image.Decode(r)
26-	if err != nil {
27-		return img, format, err
28-	}
29-
30-	switch o {
31-	case 3:
32-		img = imaging.Rotate180(img)
33-	case 6:
34-		img = imaging.Rotate270(img)
35-	case 8:
36-		img = imaging.Rotate90(img)
37-	}
38-	return img, format, nil
39-}
+67, -0
  1@@ -1,8 +1,10 @@
  2 package ui
  3 
  4 import (
  5+	"bytes"
  6 	"fmt"
  7 	"image"
  8+	"io"
  9 	"os"
 10 	"reflect"
 11 	"runtime"
 12@@ -13,6 +15,7 @@ import (
 13 	"git.sr.ht/~rockorager/vaxis"
 14 	"git.sr.ht/~rockorager/vaxis/widgets/align"
 15 	"github.com/containerd/console"
 16+	"github.com/disintegration/imaging"
 17 
 18 	"git.sr.ht/~delthas/senpai/events"
 19 	"git.sr.ht/~delthas/senpai/irc"
 20@@ -45,6 +48,8 @@ type ConfigColors struct {
 21 type Vaxis struct {
 22 	*vaxis.Vaxis
 23 	window vaxis.Window
 24+	xPixel int
 25+	yPixel int
 26 }
 27 
 28 type NotifyEvent struct {
 29@@ -693,6 +698,11 @@ func (ui *UI) InputBackSearch() {
 30 	ui.e.BackSearch()
 31 }
 32 
 33+func (ui *UI) SetWinPixels(xPixel int, yPixel int) {
 34+	ui.vx.xPixel = xPixel
 35+	ui.vx.yPixel = yPixel
 36+}
 37+
 38 func (ui *UI) Resize() {
 39 	ui.vx.window = ui.vx.Window() // Refresh window size
 40 	w, h := ui.vx.window.Size()
 41@@ -1017,3 +1027,60 @@ func (ui *UI) drawVerticalMemberList(vx *Vaxis, x0, y0, width, height int, b *bu
 42 		printString(vx, &x, y, name)
 43 	}
 44 }
 45+
 46+func (ui *UI) DecodeImage(r io.Reader) (image.Image, string, error) {
 47+	var b bytes.Buffer
 48+	tr := io.TeeReader(io.LimitReader(r, 1<<20), &b)
 49+	o := exifOrientation(tr)
 50+	r = io.MultiReader(&b, r)
 51+
 52+	img, format, err := image.Decode(r)
 53+	if err != nil {
 54+		return img, format, err
 55+	}
 56+
 57+	w, h := ui.vx.window.Size()
 58+	w = w * 9 / 10
 59+	h = h * 9 / 10
 60+	if w > 0 && h > 0 {
 61+		wp := img.Bounds().Dx()
 62+		hp := img.Bounds().Dy()
 63+		switch o {
 64+		case 6, 8:
 65+			wp, hp = hp, wp
 66+		}
 67+		cellPixW := ui.vx.xPixel / w
 68+		cellPixH := ui.vx.yPixel / h
 69+		columns := (wp + cellPixW - 1) / cellPixW
 70+		lines := (hp + cellPixH - 1) / cellPixH
 71+		if columns > w || lines > h {
 72+			sfX := float64(w) / float64(columns)
 73+			sfY := float64(h) / float64(lines)
 74+			nwp := wp
 75+			nhp := hp
 76+			switch {
 77+			case sfX < sfY:
 78+				nwp = int(sfX * float64(wp))
 79+				nhp = int(sfX * float64(hp))
 80+			case sfX > sfY:
 81+				nwp = int(sfY * float64(wp))
 82+				nhp = int(sfY * float64(hp))
 83+			}
 84+			switch o {
 85+			case 6, 8:
 86+				nwp, nhp = nhp, nwp
 87+			}
 88+			img = imaging.Resize(img, nwp, nhp, imaging.NearestNeighbor)
 89+		}
 90+	}
 91+
 92+	switch o {
 93+	case 3:
 94+		img = imaging.Rotate180(img)
 95+	case 6:
 96+		img = imaging.Rotate270(img)
 97+	case 8:
 98+		img = imaging.Rotate90(img)
 99+	}
100+	return img, format, nil
101+}