diff options
Diffstat (limited to 'internal/media/process.go')
| -rw-r--r-- | internal/media/process.go | 82 |
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) + } +} |
