[performance] move thumbnail generation to go code where possible (#3183)

* wrap thumbnailing code to handle generation natively where possible

* more code comments!

* add even more code comments!

* add code comments about blurhash generation

* maintain image rotation if contained in exif data

* move rotation before resizing

* ensure pix_fmt actually selected by ffprobe, check for alpha layer with gifs

* use linear instead of nearest-neighbour for resizing

* work with image "orientation" instead of "rotation". use default 75% quality for both webp and jpeg generation

* add header to new file

* use thumb extension when getting thumb mime type

* update test models and tests with new media processing

* add suggested code comments

* add note about thumbnail filter count reducing memory usage
This commit is contained in:
kim
2024-08-08 17:12:13 +00:00
committed by GitHub
parent 94c615d417
commit f77005128a
36 changed files with 588 additions and 215 deletions

View File

@ -21,20 +21,24 @@ import (
"cmp"
"errors"
"fmt"
"image"
"io"
"os"
"golang.org/x/image/webp"
"codeberg.org/gruf/go-bytesize"
"codeberg.org/gruf/go-iotools"
"codeberg.org/gruf/go-mimetypes"
"github.com/buckket/go-blurhash"
"github.com/disintegration/imaging"
)
// getExtension splits file extension from path.
func getExtension(path string) string {
for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {
if path[i] == '.' {
return path[i+1:]
}
}
return ""
}
// thumbSize returns the dimensions to use for an input
// image of given width / height, for its outgoing thumbnail.
// This attempts to maintains the original image aspect ratio.
@ -68,44 +72,6 @@ func thumbSize(width, height int, aspect float32) (int, int) {
}
}
// webpDecode decodes the WebP at filepath into parsed image.Image.
func webpDecode(filepath string) (image.Image, error) {
// Open the file at given path.
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
// Decode image from file.
img, err := webp.Decode(file)
// Done with file.
_ = file.Close()
return img, err
}
// generateBlurhash generates a blurhash for JPEG at filepath.
func generateBlurhash(filepath string) (string, error) {
// Decode JPEG file at given path.
img, err := webpDecode(filepath)
if err != nil {
return "", err
}
// for generating blurhashes, it's more cost effective to
// lose detail since it's blurry, so make a tiny version.
tiny := imaging.Resize(img, 64, 64, imaging.NearestNeighbor)
// Drop the larger image
// ref as soon as possible
// to allow GC to claim.
img = nil //nolint
// Generate blurhash for thumbnail.
return blurhash.Encode(4, 3, tiny)
}
// getMimeType returns a suitable mimetype for file extension.
func getMimeType(ext string) string {
const defaultType = "application/octet-stream"