summaryrefslogtreecommitdiffstats
path: root/internal/media/process.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/media/process.go')
-rw-r--r--internal/media/process.go82
1 files changed, 82 insertions, 0 deletions
diff --git a/internal/media/process.go b/internal/media/process.go
new file mode 100644
index 0000000..b295380
--- /dev/null
+++ b/internal/media/process.go
@@ -0,0 +1,82 @@
+package media
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "image/png"
+
+ "github.com/davidbyttow/govips/v2/vips"
+ thumbhash "go.n16f.net/thumbhash"
+)
+
+// ImageInfo returns the pixel dimensions and a base64-encoded thumbhash for
+// the image at path. The thumbhash is generated from a ≤100px thumbnail so
+// it is fast regardless of the original size.
+func ImageInfo(path string) (width, height int, thumbhashB64 string, err error) {
+ img, err := vips.NewImageFromFile(path)
+ if err != nil {
+ return 0, 0, "", fmt.Errorf("load %s: %w", path, err)
+ }
+ defer img.Close()
+
+ width = img.Width()
+ height = img.Height()
+
+ const maxThumbDim = 100
+ if width > maxThumbDim || height > maxThumbDim {
+ scale := float64(maxThumbDim) / float64(max(width, height))
+ if err := img.Resize(scale, vips.KernelLanczos3); err != nil {
+ return width, height, "", fmt.Errorf("resize for thumbhash: %w", err)
+ }
+ }
+
+ pngBytes, _, err := img.ExportPng(vips.NewPngExportParams())
+ if err != nil {
+ return width, height, "", fmt.Errorf("export png for thumbhash: %w", err)
+ }
+
+ goImg, err := png.Decode(bytes.NewReader(pngBytes))
+ if err != nil {
+ return width, height, "", fmt.Errorf("decode png for thumbhash: %w", err)
+ }
+
+ hash := thumbhash.EncodeImage(goImg)
+ return width, height, base64.StdEncoding.EncodeToString(hash), nil
+}
+
+// ConvertAndResize loads the image at src, optionally resizes it to width pixels
+// wide (maintaining aspect ratio), and exports it in the requested format.
+//
+// width <= 0 means no resize. format must be "webp" or "jpeg".
+func ConvertAndResize(src string, width int, format string) ([]byte, error) {
+ img, err := vips.NewImageFromFile(src)
+ if err != nil {
+ return nil, fmt.Errorf("load image %s: %w", src, err)
+ }
+ defer img.Close()
+
+ if width > 0 && width < img.Width() {
+ scale := float64(width) / float64(img.Width())
+ if err := img.Resize(scale, vips.KernelLanczos3); err != nil {
+ return nil, fmt.Errorf("resize: %w", err)
+ }
+ }
+
+ switch format {
+ case "webp":
+ buf, _, err := img.ExportWebp(vips.NewWebpExportParams())
+ if err != nil {
+ return nil, fmt.Errorf("export webp: %w", err)
+ }
+ return buf, nil
+ case "jpeg":
+ buf, _, err := img.ExportJpeg(vips.NewJpegExportParams())
+ if err != nil {
+ return nil, fmt.Errorf("export jpeg: %w", err)
+ }
+ return buf, nil
+ default:
+ return nil, fmt.Errorf("unsupported format %q: must be webp or jpeg", format)
+ }
+}